繁体   English   中英

线程同步和volatile关键字

[英]thread synchronization and volatile keyword

我知道堆栈溢出中有很多可用的主题,但是从理论上讲,我尝试学习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.

但是在此示例中,如果您在线程A将值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

输出中没有矛盾:

  • 线程B可以启动,看到计数为0,构建字符串“ threadB 0” ,然后进入“睡眠”状态。 然后,当他醒来时,将其打印到控制台(尽管此时实际计数值为6)

  • 线程C可以用6(而不是0)完全相同。

另外,增量操作count++不是原子的,count ++等于

int temp = count + 1;
count = temp;

如果两个线程同时增加计数,则值可能会增加1,而不是2。这就是为什么您不应该对volatile变量使用增量操作,而是使用AtomicInteger的原因

挥发性并不总是足够的

即使volatile关键字保证直接从主存储器读取所有volatile变量的读操作,并且将对volatile变量的所有写操作直接写到主存储器,在某些情况下,仍不足以声明volatile变量。

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

一旦线程需要首先读取volatile变量的值( 在上面的示例中发生) ,并基于该值为共享的volatile变量生成新值,那么volatile变量将不再足以保证正确的可见性。 在读取volatile变量与写入新值之间的短暂时间间隔会造成竞争状态 ,在此情况下 ,多个线程可能会读取volatile变量的相同值,为该变量生成一个新值,以及在写入该值时返回主内存-覆盖彼此的值。

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

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

  2. 由于缺少同步,循环执行了17次而不是15次。 挥发保证仅可见性,同步保证可见性和相互排斥。 在执行count ++(这是复合操作(读-增量-写))时,应使用同步,并且正如AdamSkywalker所建议的那样,在这种情况下AtomicInteger是您的朋友。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM