【问题标题】:Not getting any output from jobs submitted to thread pool没有从提交到线程池的作业中获得任何输出
【发布时间】:2012-05-01 14:38:41
【问题描述】:

假设我有 A 类和 B 类。A 类只有一个 main 方法,代码如下:

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
        }
    }
}

B 类扩展了 Thread,如下所示:

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";
    }
}

然而,System.out.println(xput) 除了空行之外不打印任何内容。有谁知道为什么?我的类的代码显然比这多得多,但由于我没有得到任何输出,所以我从一个小案例开始。

【问题讨论】:

  • 您的 main 方法没有实例化任何 B,我们不知道 list 是什么。
  • 您需要阅读大量有关多线程、并发和 Java 内存模型的内容。这些都是复杂而微妙的话题,我不认为只是将代码示例放在 SO 上并让人们告诉你该做什么从长远来看会有很大帮助。你真的有很多东西要学。 Goetz 所著的“Java 并发实践”是一本极好的书。

标签: java multithreading static


【解决方案1】:

此代码存在许多竞争条件,因为它们都在更新相同的static String returnValue。此外,当调用 System.out.println(xput) 时,线程实际上可能还没有运行。您需要使用 future.get() 方法来等待每个线程完成,并且您不能在将它们提交到线程池的同一循环中执行此操作。

由于 4 个线程将同时运行,并且它们都在更新相同的 static 字段,因此您需要围绕该变量提供一些同步。我建议改为使用ExecutorServiceFuture 功能,而不是修改静态字段。像下面这样的东西应该可以工作:

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";
    }
}

ExecutorServiceFuture 功能允许您从线程池处理的每个作业中获取结果。您可以通过submit() Callable 类从call() 方法返回结果String(或其他对象)。

另外,你的B 应该实现Callable 而不是扩展Thread。虽然它会起作用,但这只是因为Thread 也实现了Runnable。线程池有自己的内部线程,你只需向它提交 RunnableCallable 对象。

最后,在处理列表(或任何 Java 集合)时,不要使用 for (int i 循环,而应该养成使用的习惯:

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

如果您使用for (int i,那么至少从0 到列表的size()

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

这样,如果您更改列表的大小,您也不必记住更改循环。

【讨论】:

  • 如何设置它以便在线程运行后打印语句?
  • 我已将其添加到我的答案@user1261445 中。使用 Future,您可以从 call() 方法获取结果并构建您的结果字符串。最后你只打印sb.toString()
  • +1。不错的编辑。把这个答案从一个好的答案变成了一个很好的答案!
【解决方案2】:

没有打印任何内容,因为您在设置“returnValue”之前正在检查它。拥有“returnValue”静态也意味着所有线程都将写入同一个共享变量。

如果你想为你的线程有一个返回值,让它们实现callable&lt;T&gt;而不是线程,并将它们传递给&lt;T&gt; Future&lt;T&gt; submit(Callable&lt;T&gt; task)方法。在返回的 Future&lt;T&gt; 上调用 get() 以获得您要查找的值。

【讨论】:

    最近更新 更多