user1261445 user1261445 - 7 months ago 29
Java Question

Not getting any output from jobs submitted to thread pool

Say I have class A and class B. class A has just a main method with the following code:

public class A{
public static void main(String[] args){
String xput = "";
ExecutorService pool = Executors.newFixedThreadPool(4);
for(int i = 1; i < number; i++){
pool.submit(new B(list.get(i-1)));
xput = B.returnValue;
System.out.println(xput);//testing purposes
}
}
}


class B extends Thread, and looks like this:

public class B extends Thread{

static String returnValue = "";

public B(String x){
super(x);
}

public void run(){
double x = 20;
returnValue += "Grand total: " +
NumberFormat.getCurrencyInstance().format(x) + "\n";
}
}


Yet
System.out.println(xput)
does not print anything except an empty line. Anyone know why? My classes have a lot more code than this obviously, but since I'm not getting any output, I'm starting with a small case.

Answer Source

This code suffers from a number of race conditions since they are all updating the same static String returnValue. Also the threads may actually not have run yet when the System.out.println(xput) is called. You would need to use the future.get() method to wait for each of the threads to finish and you can't do that in the same loop where you submit them to the thread-pool.

Since 4 threads will be running at once, and they all are updating the same static field, you need to provide some synchronization around that variable. What I would recommend instead is to use the Future features of the ExecutorService instead of modifying the static field. Something like the following should work:

List<Future<String>> futures = new ArrayList<Future<String>>();
for(int i = 1; i < number; i++){
    B b = new B(list.get(i - 1));
    // submit the job b add the resulting Future to the list
    futures.add(pool.submit(b));
}
// all of the jobs are submitted now
StringBuilder sb = new StringBuilder();
for (Future<String> future : futures) {
   // now join with each of the jobs in turn and get their return value
   sb.append(future.get());
}
System.out.println(sb.toString());

// you should implement Callable _not_ extend thread
public class B implements Callable<String> {
    public String call(){
        ...
        return "some string";
    }
}

The Future features of the ExecutorService allow you to get results from each of the jobs that were processed by the thread-pool. You can submit() Callable classes which can return a result String (or other object) from the call() method.

Also, your B should implement Callable not extend Thread. Although it would work, it is only because Thread implements Runnable as well. The thread-pool has its own internal threads and you only submit Runnable or Callable objects to it.

Lastly, instead of using a for (int i loop when processing lists (or any Java collection), you should get into the habit of using:

 for(String x : list) {
    B b = new B(x);
    ...

If you have to use the for (int i then at least go from 0 to the size() of the list:

 for(int i = 0; i < list.size(); i++) {

That way if you change the size of list you don't have to remember to change your loop as well.