Java 这段代码不是线程安全的吗?

Java 这段代码不是线程安全的吗?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我希望这段代码是线程安全的。我运行了几次,但得到了不同的结果。但是,如果我取消注释sleep(1000)部分,它每次打印10000次(至少从测试运行的结果中) 那怎么了?这可能与thread.join()有关吗 公共类测试实现可运行{ 私人INTX; 公共同步的无效运行(){ x++; } 公共静态void main(字符串参数[]){ 测试=新测试(); 线程=空; 对于(int i=0;i

我希望这段代码是线程安全的。我运行了几次,但得到了不同的结果。但是,如果我取消注释
sleep(1000)
部分,它每次打印10000次(至少从测试运行的结果中)

那怎么了?这可能与
thread.join()
有关吗

公共类测试实现可运行{
私人INTX;
公共同步的无效运行(){
x++;
}
公共静态void main(字符串参数[]){
测试=新测试();
线程=空;
对于(int i=0;i<10000;i++){
螺纹=新螺纹(测试);
试一试{
thread.join();
}捕获(中断异常e){}
thread.start();
}
//试一试{
//睡眠(1000);
//}catch(InterruptedException e){
//e.printStackTrace();
//      }
System.out.println(test.x);
}
}

编辑:哎呀,我的错。我误解了线程如何连接函数。在
run()
方法上同步是个坏主意。

您没有将synchronized添加到run方法。每个线程都有自己的线程

您必须同步可变的共享数据。在您的例子中,这是
整数x
。您可以同步get/set或使用
AtomicInteger

应在之后调用

join()。这只有在线程启动后才有意义


想必您的
Thread.sleep()
调用实际上会等待足够长的时间,等待所有线程(实际上您没有加入)完成。如果没有它,当您打印出
x

的值时,线程可能不会全部完成。这里有两个问题:

  • 主线程在所有辅助线程之前完成的一种争用状态

  • 内存可见性问题,其中主线程不能保证看到更新的x值

线程#连接是使用对象#wait实现的。使用的条件变量是线程上的活动标志:

groovy:000> new Thread().isAlive()
===> false
join在线程启动之前检查活动标志,因此isAlive返回false,join在线程启动之前返回。计数器最终仍然会递增,但由于该线程没有发生连接,因此主线程可能会在所有线程都可以执行之前打印出x的结果

添加睡眠使所有线程都有足够的时间来完成,在主线程打印出x时,x是您所期望的

除了争用条件外,还存在内存可见性问题,因为主线程直接访问x,并且没有使用与其他线程相同的锁。您应该使用
synchronized
关键字向Runnable添加访问者:

public class Test implements Runnable{

    private int x;

    public synchronized void run(){
        x++;
    }

    public synchronized int getX() {
        return x;
    }
并将主要方法更改为使用访问器:

    System.out.println(test.getX());
内存可见性问题可能并不明显,因为它们取决于JVM在缓存和优化方面的积极程度。如果您的代码在生产环境中针对不同的JVM实现运行,并且您没有充分防范这些问题,那么您可能会看到无法在PC上本地复制的错误


使用AtomicInteger将简化此代码,并允许在删除同步时解决内存可见性问题。

为什么在
thread.start()之前有
thread.join()
join()
表示“阻塞直到线程结束”。这只有在线程启动后才有意义。在run方法上进行同步在这里不是一个真正的问题(除了主方法直接访问x的部分,正如我的回答所指出的);由于所有线程共享同一个测试同步实例,因此可以正常工作。感谢您的见解。这真的很有帮助。
    System.out.println(test.getX());