0

OKAY, I'm sorry for the Title. I really didn't know how to phrase this. I'll try better here.

So, I have 2 Java classes. We'll call them FirstClass and SecondClass (which implemts Runnable). In FirstClass, I'm doing some stuff and then I'm creating 4 threads.

Thread t1 = new Thread (new SecondClass(s1));
Thread t2 = new Thread (new SecondClass(s2));
Thread t3 = new Thread (new SecondClass(s3));
Thread t4 = new Thread (new SecondClass(s4));

s1, s2, s3 and s4 are all of type String and hold individual values.

Then I start the threads straight away.

t1.start();
t2.start();
t3.start();
t4.start();

Then in my SecondClass, I am taking these strings in the default constructor as follows

HashMap<String, Integer> map;

public SearchResults(String s) {
    map.put(s, 0);
}

in the run() method I am doing the following

public void run() {
    try {   
        System.out.println(map);
    } catch (Exception e) {
        // TODO: handle exception
    }           
}

So the result of this useless program is that map is printed out 4 times with 4 different values.

I am wondering how I can return one instance of map that has all the values that t1 put into it and all the values that t2 put into it etc etc. They are all working off the same variable, map, but each Thread does it's own thing it seems.

Could I maybe, let the threads execute and then when they are all finished, return the map to another class or something? I really don't know much about Threads so this has been confusing me. Any help would be greatly appreciated.

Gray
  • 115,027
  • 24
  • 293
  • 354
UserBruiser
  • 155
  • 1
  • 2
  • 17
  • 1
    Research the keyword "static" and the type ConcurrentHashMap. – pamphlet Apr 10 '13 at 14:08
  • Have you think about TreadLocal variable. [In this question](http://stackoverflow.com/questions/1490919/purpose-of-threadlocal) [or this one](http://stackoverflow.com/questions/817856/when-and-how-should-i-use-a-threadlocal-variable) – icrovett Apr 10 '13 at 14:09

5 Answers5

3

There are number of ways to do this. A better solution would be to switch and use ExecutorService instead of forking the threads yourself. You then can then implement Callable<Map> (instead of Runnable) in your SecondClass and return the maps that each of the jobs create.

Something like:

// create a thread pool with as many workers as there are jobs
ExecutorService threadPool = Executors.newCachedThreadPool();
List<Future<Map<String, Integer>>> futures =
    new ArrayList<Future<Map<String, Integer>>>();
futures.add(threadPool.submit(new SecondClass(s1)));
futures.add(threadPool.submit(new SecondClass(s2)));
futures.add(threadPool.submit(new SecondClass(s3)));
futures.add(threadPool.submit(new SecondClass(s4)));
// once we have submitted all jobs to the thread pool, it should be shutdown
threadPool.shutdown();
...
Map<String, Integer> all = new HashMap<String, Integer>();
for (Future<Map<String, Integer>> future : futures) {
    // future.get() throws an exception if your call method throws
    all.putAll(future.get());
}

So then your SecondClass implements Callable:

public Map<String, Integer> call() {
    ...
    return map;
}

Some other mechanisms that you could use include:

  • Using a shared ConcurrentHashMap that was passed in (better) or static (not as good)
  • Have your threads put their results on a BlockingQueue when they are done
  • Join with the threads and then call a method on the class that gets the Map that was created.
Gray
  • 115,027
  • 24
  • 293
  • 354
  • Thanks, Gray! I am a little confused with your above code in relation to mine though (not your fault). The thread pool holds a certain amount of threads (10 it seems) and these are then executed and the program waits until they are finished, right? So how would I get multiple threads to do the same job but with different _String_ variables whilst all the time adding to the same HashMap? Create list of jobs to do and execute the entire pool rather than threads individually and wait until they finish? I'm sorry, I will go ahead and read about _ExecutorService_ but any extra pointers? – UserBruiser Apr 10 '13 at 14:32
  • oh wait, I jumped the gun and posted before your edit. That makes more sense to me now, thank you. Would the above be done in **FirstClass** and then in my **SecondClass**, I would implement _Callable_ rather than _Runnable_ ? – UserBruiser Apr 10 '13 at 14:33
  • The thread-pool would be created in `FirstClass`. The `SecondClass` would implement `Callable` @UserBruiser. – Gray Apr 10 '13 at 14:34
2

You can make your map static:

private static Map<String, Integer> map = new HashMap<String, Integer>();

This will make all your SecondClass instances share the same map.

Don't forget to synchronize properly if you do this though, either by changing to another Map type or by synchronizing your writes. You can read up on that subject here, in the Java Tutorials.

Keppil
  • 45,603
  • 8
  • 97
  • 119
  • Thank you, Keppil! This works fine but I'll hold off on selecting an Answer as the _ExecutorService_ that has been mentioned is interesting and might be a better way. – UserBruiser Apr 10 '13 at 14:25
0

What you want is use Future and ExecutorService.

Give a look at its javadoc, but with it you can calculate things async and wait for the result using Future#get(). In the end you can print it all together.

Something like:

public class SearchResults implements Callable {
     // 
}
...
    ExecutorService executor = Executors.newFixedThreadPool(4);
    List<SearchResults> searchResults = new LinkedList<SearchResults>();
    List<Future> results = executor.invokeAll(searchResults);    
    Map<String, Integer> calculatedResults = new HashMap<String, Integer>();

    for (Future result : results) {
        calculatedResults.putAll(result.get());
    }
    // print calculated results
Caesar Ralf
  • 2,203
  • 1
  • 18
  • 36
0

If you want a result at the end of running all threads, you might want to look at using CyclicBarrier or a CountdownLatch.

Chris Cooper
  • 4,982
  • 1
  • 17
  • 27
0

As others are saying, you can use ConcurrentHashMap or ThreadLocal variable, using that you can share a variable in multiple threads.
Now coming to second part of your question, which is,

Could I maybe, let the threads execute and then when they are all finished, return the map to another class or something?

You can use Future, as someone already suggested, but in that case also you have to wait, if the execution is not yet complete, and how will you handle the competition of different threads and notify that is another thing. You can go ahead with this if your requirement says so.
But I suggest you to go through Observer pattern once, and check if that can help you in your requirement. One class can observe the threads, once completed it can notify. Try to implement it, if any issues let us know.

Abhinaba Basu
  • 361
  • 1
  • 9
  • How does `ThreadLocal` allow you to share a variable in multiple threads? That's the opposite of what `ThreadLocal` does. – pamphlet Apr 10 '13 at 14:19