Java 为什么该代码不会导致竞态条件?

Java 为什么该代码不会导致竞态条件?,java,multithreading,race-condition,Java,Multithreading,Race Condition,下面的代码递增线程中的静态变量,并检查其值是否递增1。这就是Assert.assertEquals(currentAVal+1,accessCounter)检查 测试持续通过10000次。但是为什么没有比赛条件导致测试失败呢? 我希望两个或更多线程在第accessCounter=accessCounter+1行递增accessCounter在断言发生之前,但这似乎没有发生 public class RunnableTest { private static int accessCount

下面的代码递增线程中的静态变量,并检查其值是否递增1。这就是Assert.assertEquals(currentAVal+1,accessCounter)检查

测试持续通过10000次。但是为什么没有比赛条件导致测试失败呢? 我希望两个或更多线程在第
accessCounter=accessCounter+1行递增
accessCounter
在断言发生之前,但这似乎没有发生

public class RunnableTest {
    private static int accessCounter = 0;

    private class Post implements Runnable {
        public void run() {
            int currentAVal = accessCounter;
            accessCounter = accessCounter + 1;
            Assert.assertEquals(currentAVal+1, accessCounter);
            System.out.println("Access counter : "+accessCounter);
        }
    }

    @Test
    public void runTest(){
        Runnable r = new Post();    
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
        for(int executorCount = 0; executorCount < 10000; ++executorCount) {  
            executor.execute(r);
        }
    }
}
公共类RunnableTest{
私有静态int-accessCounter=0;
私有类Post实现可运行{
公开募捐{
int currentAVal=访问计数器;
accessCounter=accessCounter+1;
Assert.assertEquals(currentAVal+1,accessCounter);
System.out.println(“访问计数器:“+accessCounter”);
}
}
@试验
公共无效运行测试(){
Runnable r=new Post();
ScheduledExecutorService executor=Executors.newScheduledThreadPool(4);
对于(int executorCount=0;executorCount<10000;++executorCount){
执行人。执行人(r);
}
}
}
更新:根据格雷的回答,我已经更新了代码,当我删除println语句时,我现在收到一个竞争条件(测试失败):

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import junit.framework.Assert;

public class RunnableTest {

    private static int accessCounter = 0;
    private static List<String> li = new ArrayList<String>();

    private class Post implements Runnable {
        public synchronized void run() {

            int currentAVal = accessCounter;
            accessCounter = accessCounter + 1;
            li.add(String.valueOf(currentAVal+1+","+accessCounter));

        }
    }

    @Test
    public void runTest(){

        Runnable r = new Post();    
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
        for(int executorCount = 0; executorCount < 10000; ++executorCount) {  
            executor.execute(r);
        }
        //Wait for threads to finish
        // we shut it down once we've submitted all jobs to it
        executor.shutdown();
        // now we wait for all of those jobs to finish
        try {
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(String s : li){
            Assert.assertEquals(s.split(",")[0], s.split(",")[1]);
        }
    }

}
import java.util.ArrayList;
导入java.util.List;
导入java.util.concurrent.Executors;
导入java.util.concurrent.ScheduledExecutorService;
导入java.util.concurrent.TimeUnit;
导入org.junit.Test;
导入junit.framework.Assert;
公共类RunnableTest{
私有静态int-accessCounter=0;
私有静态列表li=newarraylist();
私有类Post实现可运行{
公共同步的无效运行(){
int currentAVal=访问计数器;
accessCounter=accessCounter+1;
li.add(String.valueOf(currentAVal+1+,“+accessCounter));
}
}
@试验
公共无效运行测试(){
Runnable r=new Post();
ScheduledExecutorService executor=Executors.newScheduledThreadPool(4);
对于(int executorCount=0;executorCount<10000;++executorCount){
执行人。执行人(r);
}
//等待线程完成
//一旦我们向它提交了所有作业,我们就会关闭它
executor.shutdown();
//现在我们等待所有这些工作完成
试一试{
执行器等待终止(Long.MAX_值,时间单位为毫秒);
}捕捉(中断异常e){
e、 printStackTrace();
}
for(字符串s:li){
Assert.assertEquals(s.split(“,”[0],s.split(“,”[1]);
}
}
}

synchronized
添加到run方法会导致测试通过

答案在问题本身中。这是一种竞赛条件:
您无法保证无论您尝试向它抛出多少次线程或尝试运行它多少次,它都会发生。这就是为什么这是一个比赛条件。它是不确定的

除非你能说明原因,否则假设这一点的概率分布是完全不正确的。你不是在这里掷硬币。代码可能会在比赛公开前运行数月,我已经多次看到这种情况发生。这就是为什么种族状况很难解决,也很重要的原因


其次,您没有在场景中植入任何数量的随机噪声。如果你说让每个线程的run函数先休眠一段随机的时间,这样它们就很可能彼此一致,这会更有趣。。。但是你的线程太短了,它们很可能已经完成了,与生成和分派作业所需的时间相比,甚至从未并行运行过。

答案就在问题本身。这是一种竞赛条件:
您无法保证无论您尝试向它抛出多少次线程或尝试运行它多少次,它都会发生。这就是为什么这是一个比赛条件。它是不确定的

除非你能说明原因,否则假设这一点的概率分布是完全不正确的。你不是在这里掷硬币。代码可能会在比赛公开前运行数月,我已经多次看到这种情况发生。这就是为什么种族状况很难解决,也很重要的原因

其次,您没有在场景中植入任何数量的随机噪声。如果你说让每个线程的run函数先休眠一段随机的时间,这样它们就很可能彼此一致,这会更有趣。。。但是你的线程太短了,它们很可能已经完成了,与生成分派作业所需的时间相比,甚至从来没有并行运行过

测试持续通过10000次。但是为什么没有比赛条件导致测试失败呢

竞赛条件的定义是,你可能会遇到计时问题——这不是保证。如果在另一个体系结构上运行此功能,可能会得到完全不同的结果

然而,我不认为junit可以看到其他线程中的断言。例如,如果我改变你测试以下内容。我确实看到了值不同的次数,但测试方法看不到
失败
——测试仍然通过

if (currentAVal+1 != accessCounter) {
    System.out.println("Access counter not equal: "+accessCounter);
    Assert.fail();
}
您可能在
accessCounter
中看到正确值的一个原因是
System.out.println(…)
是一种同步方法,它(作为副产品)同步
accessCounter
的值

此外,您没有关闭executor,也没有等待executor服务实际完成。你应该这样做:

// we shut it down once we've submitted all jobs to it
executor.shutdown();
// now we wait for all of those jobs to finish
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
List<Future<?>> futures = new ArrayList<Future<?>>();
for (int executorCount = 0; executorCount < 10000; ++executorCount) {
    futures.add(executor.submit(r));
}
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
for (Future<?> future : futures) {
    // this will throw an exception if an assert happened
    future.get();
}
但这并不能解决另一个线程问题。要实际查看线程的结果,可以执行以下操作:

// we shut it down once we've submitted all jobs to it
executor.shutdown();
// now we wait for all of those jobs to finish
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
List<Future<?>> futures = new ArrayList<Future<?>>();
for (int executorCount = 0; executorCount < 10000; ++executorCount) {
    futures.add(executor.submit(r));
}
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
for (Future<?> future : futures) {
    // this will throw an exception if an assert happened
    future.get();
}
List>();
对于(int executorCount=0;executorCount<10000;++executorCount){
未来