多线程执行,为什么明明已经加了synchronized了,还是出现每次结果都不一样呢?

public class Test {

    private int a = 0;

    public static void main(String[] args) {

        Test test = new Test();

        for (int i = 0; i <= 10; i++) {

            new Thread(new Runnable() {
                @Override
                public void run() {
                    test.setA(test.getA() + 1);
                }
            }).start();
        }

        System.out.println(test.getA());
    }

    public int getA() {
        return a;
    }

    public synchronized void setA(int a) {
        this.a = a;
    }
}

如果我把synchronide 放到run方法里则执行正确? 为什么呢? 锁不是当前对象吗? 只有一个test对象啊?

阅读 4.3k
4 个回答

即使读和写都分别是原子的,读、加一(这个不涉及共享内存)、写这三个操作加在一起也不是原子的。线程依然可能在这三个操作之中被打断。

如果我把synchronide 放到run方法里则执行正确?

你咋放的?Java 有好多加 synchronize 的方式。

如果是

synchronized(test) {
    test.setA(test.getA() + 1);
}

那么就没有问题,因为此时 读、加一、写 三者作为一个整体被保护起来了。

因为只锁住了写没有锁住读。加了锁的是 set 方法,所以可以保证同一时间仅有一个线程在执行 set 方法。get 方法没有被锁住,所以可能存在多个线程同时执行 get 方法。比如有线程a,b,c,它们同时执行 get 方法读取到 1,然后再依次执行 set(2),此时最终结果为 2。若只有 a, b同时读取到1,然后依次进行 set 将值写为 2,之后 c 再执行 get 与 set,最终结果就是 3 了。

对@fefe 答案做下补充

对于这种整型变量的加减操作,建议使用private AtomicInteger a = new AtomicInteger(0);

AtomicInteger会保证其实例的线程安全行,无论读、写都是线程安全的。它还有getAndIncrement()操作模拟a++这样的操作。它比使用synchronized更加轻量。

新手上路,请多包涵

假设有A,B俩个线程,A线程先走到了test.setA(test.getA() + 1);这一步并且获取锁正在执行setA()操作,但是因为你只是在setA()这一步操作时加了锁,所以B线程任然可以执行到test.setA(test.getA() + 1);这一步,并且B线程通过test.getA()拿到的值可能任然是0,就算A线程此时已经将a的值改成1也没用了,因为B线程在拿到a的值后会将值放入到告诉缓存中,后续的 + 1计算也只是对B线程缓存中的a的值进行计算,所以即使俩个线程都跑完了,a的值任然可能是1,这里你可以直接在Volatile关键字来解决,Volatile可以参考https://blog.csdn.net/F100414...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏