0

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, i.e. 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;
}
Stochastika
  • 305
  • 1
  • 2
  • 14
  • Why? Future already exists. You're reinventing several wheels here. There's no evidence here of a second thread outside the Executor at all. It isn't true that that thread can be blocked by an Executor. Incomplete. – user207421 Sep 17 '14 at 23:18
  • Hi, @EJP, I apologize that I may not have made it clear in this simplified pseudo code snippet. The way there is a thread 'outside' of the ExecutorService is through an MessageListener that 'listens' on a JMS queue on the server. and the "// sendRequest(_idx);" in run() of the threads 'inside' of the ExecutorService is meant to send a request to trigger the server to put a new message onto that JMS queue, and therefore to trigger onMessage() on that Listener (the outsider thread). And I am not trying to re-implement a Future here. Apologize if it appears to be so. – Stochastika Sep 18 '14 at 03:47

1 Answers1

0

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
}
Lolo
  • 4,277
  • 2
  • 25
  • 24
  • 1. thanks a lot for you comment 2. You are right on both counts! I modified the psuedo code accordingly. 3. However, I have to say that the main question for me here is whether the Listener Thread outside of the ExecutorService should be blocked until all the threads inside of the ExecutorService completes as I had experienced and if so why and how can I get around it. The code snippet is just to clarify what I am trying to ask. I sincerely apologize for its inaccuracy. – Stochastika Sep 18 '14 at 15:29