我编写了以下程序来了解赛车:
import java.util.concurrent.*;
class RaceCount
{
static int count = 0;
public static void main(String [] args)
{
ExecutorService executor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 1000; i++)
{
executor.submit(new Callable<String>() {
public String call() throws Exception {
count++; return "Incremented";
}
});
}
executor.shutdown();
System.out.println(count);
}
}
很明显,计数少于1000。因此,我将call()方法签名更改为:
public synchronized String call() throws Exception {
但是,结果仍然少于1000。如果我使用 newFixedThreadExecutor(1000)而不是 newSingleThreadExecutor ,那么即使 call()方法不带有 synchronized 关键字作为前缀。
因此,我的查询是:
1.在 newSingleThreadExecutor 情况下如何同步线程?
2.为什么在使用 newFixedThreadExecutor 时不需要同步?
答案 0 :(得分:3)
您的问题不是由于比赛情况引起的。发生这种情况仅仅是因为executor.shutdown()
在返回之前没有等待完全关闭。
这来自java.util.concurrent.ExecutorService.shutdown()
的javadocs:
...
此方法不等待先前提交的任务完成执行。使用awaitTermination可以做到这一点。
换句话说,System.out.println(count)
在某些任务运行之前运行(尽管它必须在所有任务提交之后运行)。
我对您的代码做了一些小的改动,以使这一事实显而易见:
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 1000; i++) {
int e = i;
executor.submit(new Callable<String>() {
public String call() throws Exception {
System.out.println("Executing " + e);
count++;
return "Incremented";
}
});
}
executor.shutdown();
System.out.println("Count: " + count);
}
输出如下:
...
Executing 835
Executing 836
Executing 837
Count: 837 <----- Printed before all tasks are run
Executing 838
Executing 839
Executing 840
Executing 841
...
这清楚地表明,在您读取count
变量之后,任务继续运行。
如果需要确保在读取更新后的值之前已执行任务,则可能需要使用awaitTermination
,如下所示:
executor.shutdown();
executor.awaitTermination(3, TimeUnit.SECONDS); //Pick an appropriate timeout value
System.out.println("Count: " + count);
答案 1 :(得分:1)
有关关机的部分只是解决方案的一半。 “公共同步的字符串call()”将对调用进行同步,以便只有一个线程可以同时执行一个实例的调用,但是通过“ executor.submit(new Callable()”),您可以拥有1000个调用实例。实际上没有同步。 您可以在循环外将其更改为“ Callable call = new Callable()...”。还有“ executor.submit(call);”内部,以便您拥有一个同步的呼叫实例。或从“ int i”更改为“ AtomicInteger i”,从++ i更改为i.incrementAndGet();