线程同步和volatile关键字

时间:2016-04-26 11:15:32

标签: java multithreading

我知道有很多主题可用于堆栈溢出但是当我试图从理论上学习volatile关键字时我相信它对我很清楚但是当我尝试运行以下示例时我的理解失败或者我无法想象顺序。

了解volatile-> Making variable as volatile for an instance, makes it non cacheable for threads that means whichever thread is accessing the volatile variable it has to flush the changes into the main memory immediately so that changes should be visible to other thread.

但是在这个例子中,如果你看到输出当threadA将值写为6被计为volatile时它应该被刷新到主内存中,所以当线程B看到这个更改时它应该看到更改但是线程B显示2而不是6或7

我无法将线程可视化,请你帮我看一下线程执行的发生方式。

public class PerfectExampleToUnderstandVolatile {
    public static void main(String[] args) {

        ThreadSample sample = new ThreadSample();
        Thread thread = new Thread(sample,"threadA");
        Thread thread1 = new Thread(sample,"threadB");
        Thread thread2 = new Thread(sample,"threadC");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

class ThreadSample implements Runnable {
        volatile int count;

    public ThreadSample() {

    }

    @Override
    public void run() {
        while (count < 15) {
            System.out.println(Thread.currentThread().getName() + " " + count);
            count++;
        }

    }

}

输出

**threadA 0**
threadA 1
threadA 2
threadA 3
threadA 4
threadA 5
**threadA 6** at this point thread A writes 6 
**threadB 0** so here thread B should show 6 
threadA 7
**threadC 6** same should be here
threadC 9
threadA 10
threadB 11
threadB 13
threadB 14
threadA 12
threadC 11

3 个答案:

答案 0 :(得分:7)

输出中没有矛盾:

  • 线程B 可以启动,看到计数为0,构建字符串“threadB 0”并转到“睡眠”。然后,当他醒来时,他将它打印到控制台(虽然那一刻真正的计数值是6)

  • 主题C 可以与6完全相同而不是0。

此外,递增操作count++不是原子的,count ++等于

int temp = count + 1;
count = temp;

如果两个线程同时递增计数,则值可能会递增1而不是2.这就是为什么不应对volatile变量使用递增运算,而是使用AtomicInteger

答案 1 :(得分:0)

挥发性并不总是足够

即使volatile关键字保证直接从主内存中读取volatile变量的所有读取,并且所有对volatile变量的写入都直接写入主内存,仍然存在声明变量不足的情况。挥发性的。

实际上,如果写入变量的新值不依赖于其先前的值,则多个线程甚至可以写入共享的volatile变量,并且仍然具有存储在主内存中的正确值。换句话说,如果将值写入共享volatile变量的线程首先不需要读取其值来计算其下一个值。

一旦线程需要首先读取volatile变量的值,在上面的例子中发生,并且基于该值为共享volatile变量生成一个新值,一个volatile变量不足以保证正确的可见度。读取volatile变量与写入新值之间的时间差短,会产生竞争条件,其中多个线程可能读取volatile变量的相同值,为该变量生成新值变量,并在将值写回主存储器时 - 覆盖彼此的值。

多个线程递增同一个计数器的情况恰好是volatile变量不够的情况。

答案 2 :(得分:0)

  1. 线程调度问题,线程调用System.out.println()与计数器值和值之间打印在控制台上的其他线程增加了计数器可以运行

  2. 由于缺乏同步,您的循环执行了17次而不是15次。 Volatile保证只有可见性,同步保证可见性和互斥。正如你在做count ++,这是复合动作(read-increment-write),应该使用同步,而AdamSkywalker建议AtomicInteger是你的朋友场景。