为什么volatile关键字没有帮助,因为它提供了对共享变量的正确读写

Hay*_*yan -2 java multithreading volatile

这是我的简单类,它在单独的线程中递增共享变量:

public class Main {

    private volatile int count;

    public static void main(String[] args) {
        Main main = new Main();
        main.doJob();

    }

    private void doJob() {
        Runnable runnable1 = () -> {
            for (int i = 0; i < 10000; i++) {
                incrementCount();
            }
        };

        Runnable runnable2 = () -> {
            for (int i = 0; i < 10000; i++) {
                incrementCount();
            }
        };

        Thread t1 = new Thread(runnable1);
        Thread t2 = new Thread(runnable2);
        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("------------------ count is : " + count);
    }

    private void incrementCount() {
        count = count + 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在任何人都可以告诉我为什么volatile关键字不能帮助我产生正确的结果,我希望它是20000?

但是,当我将方法声明更改为private syncronized void incrementCount(),一切都按预期进行时,它打印20000甚至没有volatile关键字.

小智 7

关键在于声明

count = count + 1;
Run Code Online (Sandbox Code Playgroud)

不是原子操作.因此,两个线程可以查看count变量的相同旧值,然后计算新值.甚至更多的一个线程可以在另一个线程读取和写入之间进行多个计算周期.

  • 它可见的事实并不意味着你不能有竞争条件.如果两个线程在完全相同的时间运行完全相同的代码(现在,多个核心是标准的很可能),它们将获取相同的值,同时递增它,并同时写回.这样,它会增加1,即使它应该增加2.在这样的情况下,最好使用`AtomicInteger`. (2认同)

Yog*_*h_D 6

该volatile关键字保证可视性.

在哪里作为synchronized保证相互排他性.

这意味着当变量被标记为volatile所有线程必须访问最新值时.它没有缓存.想象一下多核/多处理器机器,每台机器都有自己的缓存.随着volatile每次线程读取它保证是最新和最伟大的变量的值.

在同一场景中,每个线程都可以更新它.

现在,在您的情况下count = count + 1,发生了多个操作.

读取值count,增加它,然后设置为变量值count.

在这一点,如果count是volatile在时间说t线程1次的更新呢,再说1.然后在t+1说线程2和线程3要更新它,挥发性保证这两个线程看到价值1.然而两者都将增加1,并把它放回去.所以你实际上失去了一个增量.

什么synchronized保证是t+1,只有其中一个线程说线程2将进入它,读取值1,将其增加到2,并将其设置回变量计数.然后线程3可以输入这个同步的代码块来读取值2,将其增加到3,并将其设置回count

  • @HaykMkhitaryan最简单的例子:你想让一个线程停止循环.您可以通过在每次迭代时检查布尔标志的值,并在循环必须停止时设置此布尔标志来实现.或者,您只有一个线程修改变量,但您希望所有其他线程都能看到最新值. (2认同)