【问题标题】:Java volatile variable doesn't behave correctly.Java volatile 变量的行为不正确。
【发布时间】:2013-04-23 18:55:21
【问题描述】:
public class MyThread
{
    volatile static int i;

    public static class myT extends Thread
    {
        public void run ()
        {
            int j = 0;
            while(j<1000000){
                i++;
                j++;
            }
        }
    }

    public static void main (String[] argv)
    throws InterruptedException{
            i = 0;

            Thread my1 = new myT();
            Thread my2 = new myT();
            my1.start();
            my2.start();

            my1.join();
            my2.join();

            System.out.println("i = "+i);
    }
}

由于 volatile 建立了happens-before关系,所以 i 的最终值严格来说应该是 2000000。但是,实际结果与变量 i 没有 volatile 没有什么不同。谁能解释为什么它在这里不起作用?由于 i 被声明为 volatile,因此应保护它免受内存不一致的影响。

【问题讨论】:

    标签: java concurrency volatile


    【解决方案1】:

    谁能解释为什么它在这里不起作用?由于 i 被声明为 volatile,因此应避免内存不一致。

    它受到保护,但不幸的是i++ 不是原子操作。它实际上是读取/递增/存储。所以volatile 不会让你摆脱线程之间的竞争条件。您可能会从程序中得到以下操作顺序:

    1. 线程 #1 读取 i,得到 10
    2. 紧接着,线程 #2 读取 i,得到 10
    3. 线程 #1 将 i 增加到 11
    4. 线程 #2 将 i 增加到 11
    5. 线程 #1 将 11 存储到 i
    6. 线程 #2 将 11 存储到 i

    如您所见,即使发生了 2 个增量并且值已在线程之间正确同步,竞争条件意味着该值仅增加了 1。请参阅此 nice looking explanation。这是另一个很好的答案:Is a volatile int in Java thread-safe?

    您应该使用AtomicInteger,它允许您安全地从多个线程递增。

    static final AtomicInteger i = new AtomicInteger(0);
    ...
            for (int j = 0; j<1000000; j++) {
                i.incrementAndGet();
            }
    

    【讨论】:

    • 只有i 需要是AtomicIntegerj 纯粹是线程本地的。
    • 我不知道如何回答@OneZero。将其声明为 volatile 将不起作用,因为 ++ 不是原子的。您可以在每次更新或使用AtomicInteger 时对其进行同步。仅仅做到volatile 是不够的。
    • @OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… 应该解释一下volitile 的用途。不是这个。
    • @OneZero 非易失性读取可以获取一些位作为预写值,而其他位则来自写后值,但仅适用于底层内存上大于 word 的类型。 double 是最常见的罪犯。一般来说,大多数并发问题对于volatile 来说太过分了,所以请查看synchronizewait
    • @Gray 在实践中构建这个千年的任何东西,可能是真的。不过,我认为保证不在语言规范中。也许更实际的保证是写顺序被保留,所以如果你有一个double data 和一个volitile boolean ready,并且代码首先设置data,然后设置ready = true,那么可以肯定的是,在你看到ready变为真,data 也将被设置,即使在不同的线程中。如果没有volatile 关键字,则无法保证这一点,您可以在其中看到ready true,然后data 的值可能没有从写入线程传播。
    猜你喜欢
    • 2012-03-07
    • 1970-01-01
    • 2013-04-14
    • 1970-01-01
    • 1970-01-01
    • 2015-07-20
    • 2021-11-11
    • 2016-12-13
    • 2012-01-19
    相关资源
    最近更新 更多