简体   繁体   中英

JAVA: Execution Order between Callables submitted to ExecutorService and Threads Outside of the ExecutorService

A simplified version of what I am build has the following:

  1. A pool of threads (an ExecutorService), containing threads, each of which
    • Sends an request message onto a server
    • Waits for a the request fullfiled by continous checking on a shared data structure
  2. Outside of this thread pool, I have a MessageListener thread that
    • listens to the server.
    • Once the request is fufiled, it will raise a 'flag' in the shared data structure
    • Thread in in the pool previously mentioned will notice this. And hence continue to complete itself.

I found that the MessageListener thread is ONLY executed AFTER the ExecutorService is terminated, not running concurrently with it, ie threads in the ExecutorService are blocking the MessageListener thread from running. This is obviously undesirable for my purpose.

I wonder if someone can provide me with some pointers that whether my conclusion is valid and if so, why? and what is a good approach to get around it.

I pasted some semi-psudeo code below to further explain.

public static final int NUM_TASK = 10;
public static final int TIME_OUT = 5000;
public static boolean[] shared_data = new boolean[NUM_TASK];

public static void main(String[] args) throws InterruptedException{

    // CREATE the receiver which will be set as an LISTENER for a JMS consumer queue that
    //  later the tasks in the Executor Service will send request to
    setupJMSProducerConsumer();

    // CREATE the Executor Service (pool)
    ExecutorService fixThreadPoolES = Executors.newFixedThreadPool(10);
    List<Future<String>> futures = new ArrayList<Future<String>>();

    for(int i = 0; i < NUM_TASK; i++){

            // Submit a Callable task to Replay the file
            MyTask task = new MyTask(i);                        
            futures.add(fixThreadPoolES.submit(task));
    }

    // GATHER the result here, based on futures
    // gatherResult(futures);

    // TERMINATE the ExecutorService      
    fixThreadPoolES.shutdown();
    fixThreadPoolES.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}

private static class MyTask implements Callable<String> {

    private static final int WAIT_TIME_SLOT = 1000;
    int _idx = -1;

    public MyTask (int idx){
        _idx = idx;
    }

    @Override
    public String call() throws Exception {
        return run();
    }       

    private String run() throws InterruptedException{

        // SEND request
        // sendRequest(_idx);

        // WAIT until the corresponding result in the shared data is set to 'true' by the corresponding LISTENER 
        int timeWaited = 0;
        while (getSharedData(_idx) != true){
            timeWaited += WAIT_TIME_SLOT;
            Thread.sleep(WAIT_TIME_SLOT);
            if (timeWaited > TIME_OUT){
                return "Listener timed out, flag not raised, task incompleted for: " + _idx;
            }
        }

        return "Listener raised the flag, task completed for: " + _idx;                     
    }
}

public static class MyMsgReceiver implements MessageListener{

    @Override
    public void onMessage(Message msg) {
        String msgTxt = null;
        try {
            msgTxt = ((javax.jms.TextMessage) msg).getText();
        } catch (JMSException e) {
            e.printStackTrace();
            return;
        }

        for (int i = 0; i < NUM_TASK; i++){
            if (msgTxt.contains(String.valueOf(i))){
                setSharedData(i, true);;
            };
        }
    }           
}

private static void setupJMSProducerConsumer(){
    com.tibco.tibjms.TibjmsConnectionFactory factory = new com.tibco.tibjms.TibjmsConnectionFactory(TIBCO_URL);     
    Connection connection = factory.createConnection(TIBCO_USER, TIBCO_PASS);
    Session session = connection.createSession(false, 24);
    MessageConsumer consumer = session.creatConsumer(session.createQueue(QUEUE_NAME));

    MyMsgReceiver receiver = new MyMsgReceiver();
    consumer.setMessageListener(receiver);
}

public static synchronized boolean getSharedData(int idx) {
    return shared_data[idx];
}

public static synchronized void setSharedData(int idx, boolean val) {
    shared_data[idx] = val;
}

I see two things wrong in your code:

1) You don't synchronize the access to shared_data , therefore you're not performing atomic reads and writes to the array and this will lead to unpredicatble results. Any access to the shared array should be within a synchronized(shared_data) { } block, or more conveniently using synchronized getSharedData(int i) and setSharedData(int i, boolean value) methods

2) the wait loop in MyTask.run() isn't quite right either. The way it's implemented, if the boolean flag isn't true, then the task will systematically wait until the timeout expires, then report completion of the task, when in fact it doesn't know that the task is completed. Instead, you should do something like this:

long start = System.currentTimeMillis();
long elapsed = 0L;
boolean flag = false;
while (!(flag = getSharedData(_idx)) &&
  ((elapsed = System.currentTimeMillis() - start) < TIME_OUT)) {
  Thread.sleep(1L);
}
if (flag) {
  // handle completion
} else {
  // handle timeout expired
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM