Java 使用newSingleThreadExecutor消除ExecutorService中的争用条件

Java 使用newSingleThreadExecutor消除ExecutorService中的争用条件,java,executorservice,synchronized,Java,Executorservice,Synchronized,为了理解赛车,我编写了以下程序: 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

为了理解赛车,我编写了以下程序:

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。如果我使用newFixedThreadExecutor(1000)而不是newSingleThreadExecutor,那么即使call()方法的前缀没有synchronized关键字,我也会得到预期的1000。
因此,我的问题是:
1.如何在newSingleThreadExecutor的情况下同步线程?

2.当使用newFixedThreadExecutor时,为什么不需要同步

您的问题不是由于比赛条件造成的。之所以发生这种情况,仅仅是因为
executor.shutdown()
在返回之前没有等待完全关闭

这是来自
java.util.concurrent.ExecutorService.shutdown()
的javadocs:


此方法不会等待以前提交的任务完成执行。使用此选项可以完成此操作

换句话说,
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);
}

关于关机的部分只是解决方案的一半。 “public synchronized String call()”这将同步调用,以便只有一个线程可以同时执行一个实例的调用,但是使用“executor.submit(new Callable()”,您有1000个调用实例。因此实际上没有同步。
您可以在循环外部将其更改为“Callable call=new Callable()…”,在循环内部更改为“executor.submit(call);”,这样您就有了一个同步的调用实例。或者从“int i”更改为“AtomicInteger i”,从++i更改为i.incrementAndGet();

请参见
executor.waitemination()
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
...
executor.shutdown();
executor.awaitTermination(3, TimeUnit.SECONDS); //Pick an appropriate timeout value
System.out.println("Count: " + count);