3

I have a program which is listening for random numbers. It is hooked up to a publisher which gives me a number and a new count and every time I get an update, I'm storing the current count for that number in a HashMap.

I also have an SSL server listening for requests. When a request comes in asking "how many 7's do we have" I just return the value in my HashMap.

Now I want to add logic that says, if we have 0 occurrences of that number, wait until we get one, and return the count at that point. However I'm struggling because of the limitation on the Thread's run method, that it must be a void. I wonder if there is anyway to just declare my method as one that always launches a new thread, or maybe a better way to handle it than what I am doing. Here is what I have:

private static volatile HashMap<Integer, Integer> occurenceMap= new HashMap<Integer, Integer>();

public synchronized static int getNumOccurrences(final Integer number) {

    try { 
    (new Thread() {

        public void run() {

            Integer occurrences = occurenceMap.get(number); 
            if ( occurrences != null && occurrences > 0 ) {
              // here I would like to just return occurences;
            } else {
                CountDownLatch latch = new CountDownLatch(1); 
                pendingList.put(number, latch);
                latch.await();  
                // elsewhere in the code, I call countdown when I get a hit
                pendingList.remove(number);

                // once we've counted down, I would like to return the value
             }
           }
       }).start();
   } catch ( Throwable t ) { }
}

However, I can't put return statements in the run method. So how is this best done?

Thank you!

sally
  • 187
  • 2
  • 3
  • 11

2 Answers2

0

You'd need some kind of external structure to store the number, like this

// declared outside your runnable
final AtomicInteger result = new AtomicInteger(0);

// in your run method
// return value; // doesn't work, can't return
result.set(value);

So adding it into yours, you get this

Note that my comments start with // C:

private static volatile HashMap<Integer, Integer> occurenceMap= new HashMap<Integer, Integer>();


    public synchronized static int getNumOccurrences(final Integer number) {
        // C: here's a container to use inside the runnable
        // C: must be final to use inside the runnable below
        final AtomicInteger result = new AtomicInteger(0);
        try { 
        // C: keep a rerefence to the thread we create
        Thread thread = new Thread() {

            public void run() {

                Integer occurrences = occurenceMap.get(number); 
                if ( occurrences != null && occurrences > 0 ) {
                    result.set(occurences); // C: we found what we're looking for
                    return; // C: so get out of the run method
                } else {
                    CountDownLatch latch = new CountDownLatch(1); 
                    pendingList.put(number, latch);
                    latch.await();  
                    // elsewhere in the code, I call countdown when I get a hit
                    pendingList.remove(number);

                    // once we've counted down, I would like to return the value
                    result.set(1); // C: I'm not sure what you want to return here
                    return; // C: but I'm sure you can figure that out...
                 }
               }
           });
           thread.start(); // C: now start the thread
           thread.join(); // C: join the thread, waiting for it to finish
       } catch ( Throwable t ) { }
       return result.get(); // C: now return the int from the container
    }
corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • Okay that makes sense, thank you. However, I suppose I would need some kind of list data structure, in case we end up waiting for more than one - someone could request 44523, and another user could request 12 so we may need to wait on more than one. Most of my experience has been in single threaded programming, so sorry if it is a dumb question, but why do I need thread.join() in this case? – sally Nov 21 '12 at 20:59
  • If you don't `join`, you'll exit the method long before (probably) the thread completes. So it would return `0` every time, only to be updated later by the thread when it finally completes, long after your method has finished. You will need some sort of list wherever you call this from, but you won't need a list internally. It create a new thread for each method call, not reuse the same thread over and over. – corsiKa Nov 21 '12 at 21:03
  • I would have thought that the countdown latch enforces the wait so that the method won't exit. No? Thank you for your help – sally Nov 21 '12 at 21:10
  • The countdown latch forces the thread to take much longer than the method. The `join` forces the method to say "Wait for this thread to finish before returning." – corsiKa Nov 21 '12 at 22:58
  • I see. Does it join it to the main thread or the thread that calls it? I have my main thread which is handling the listener piece, and then another thread waiting for client requests on a socket. The one which is listening for client requests is the thread that will actually activate this method, and the one I would like to join it to. Is that how `join` will work? – sally Nov 22 '12 at 02:32
0

Another way to result values from your Thread execution it is to use the Executors thread-pools which allow you to submit a Callable:

// create a thread pool with 10 workers
ExecutorService threadPool = Executors.newFixedThreadPool(10);
List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
for (Job job : jobsToDo) {
    futures.add(threadPool.submit(new Callable<Integer>() {
       public Integer call() {
          ...
       }
     }));
}
// after submitting the jobs, you need to shutdown the queue
threadPool.shutdown();
// then you can get the results
for (Future<Integer> future : futures) {
    // this will throw if your call method throws
    int value = future.get();
}
Gray
  • 115,027
  • 24
  • 293
  • 354