简体   繁体   English

线程同步和volatile关键字

[英]thread synchronization and volatile keyword

I know there is lot of topic is available in stack overflow but as I trying to learn about volatile keyword theoretically i believe its clear to me but when i tried running the following example my understanding is failed or i am not able to visualize the sequence. 我知道堆栈溢出中有很多可用的主题,但是从理论上讲,我尝试学习volatile关键字时,我相信它对我来说很清楚,但是当我尝试运行以下示例时,我的理解失败了,或者我无法可视化序列。

Understanding of 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. 了解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.

But in this example if you see the output when threadA write the value as 6 being count as volatile it should be flushed into main memory so when thread B see this changes it should see the changes but thread B is showing 2 instead of 6 or 7 但是在此示例中,如果您在线程A将值6记为volatile时看到输出,则应将其刷新到主内存中,因此当线程B看到此更改时它应该看到更改,但是线程B显示2而不是6或7

I am not able to visualize the thread can you please help me to visualize how thread execution happens. 我无法可视化线程,请您帮我可视化线程执行的过程。

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++;
        }

    }

}

Output 输出量

**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

There are no contradictions in the output: 输出中没有矛盾:

  • Thread B can start, see that count is 0, build a string "threadB 0" and go to 'sleep'. 线程B可以启动,看到计数为0,构建字符串“ threadB 0” ,然后进入“睡眠”状态。 Then, when he awakens, he prints it to console (though real count value is 6 by that moment) 然后,当他醒来时,将其打印到控制台(尽管此时实际计数值为6)

  • Thread C can do exactly the same with 6 instead of 0. 线程C可以用6(而不是0)完全相同。

Also, increment operation count++ is not atomic, count++ is equal to 另外,增量操作count++不是原子的,count ++等于

int temp = count + 1;
count = temp;

In case if two threads increment count simultaneously, it is possible that value would be incremented by 1, not by 2. That's why you should not use increment operation on volatile variable, use AtomicInteger instead. 如果两个线程同时增加计数,则值可能会增加1,而不是2。这就是为什么您不应该对volatile变量使用增量操作,而是使用AtomicInteger的原因

volatile is Not Always Enough 挥发性并不总是足够的

Even if the volatile keyword guarantees that all reads of a volatile variable are read directly from main memory, and all writes to a volatile variable are written directly to main memory, there are still situations where it is not enough to declare a variable volatile. 即使volatile关键字保证直接从主存储器读取所有volatile变量的读操作,并且将对volatile变量的所有写操作直接写到主存储器,在某些情况下,仍不足以声明volatile变量。

In fact, multiple threads could even be writing to a shared volatile variable, and still have the correct value stored in main memory, if the new value written to the variable does not depend on its previous value. 实际上,如果写入变量的新值不依赖于先前的值,则多个线程甚至可能正在写入一个共享的volatile变量,并且仍将正确的值存储在主存储器中。 In other words, if a thread writing a value to the shared volatile variable does not first need to read its value to figure out its next value. 换句话说,如果线程首先将值写入共享的volatile变量,则不需要先读取其值即可找出下一个值。

As soon as a thread needs to first read the value of a volatile variable, which happens in above example , and based on that value generate a new value for the shared volatile variable, a volatile variable is no longer enough to guarantee correct visibility. 一旦线程需要首先读取volatile变量的值( 在上面的示例中发生) ,并基于该值为共享的volatile变量生成新值,那么volatile变量将不再足以保证正确的可见性。 The short time gap in between the reading of the volatile variable and the writing of its new value, creates a race condition where multiple threads might read the same value of the volatile variable, generate a new value for the variable, and when writing the value back to main memory - overwrite each other's values. 在读取volatile变量与写入新值之间的短暂时间间隔会造成竞争状态 ,在此情况下 ,多个线程可能会读取volatile变量的相同值,为该变量生成一个新值,以及在写入该值时返回主内存-覆盖彼此的值。

The situation where multiple threads are incrementing the same counter is exactly such a situation where a volatile variable is not enough. 多个线程递增同一计数器的情况恰好是volatile变量不足的情况。

  1. Problem with thread scheduling, between the thread calls System.out.println() with counter value and value prints on console other threads which increases the counter could run 线程调度的问题,在具有计数器值的线程调用System.out.println()和在控制台上打印值的其他线程之间,这会增加计数器的运行

  2. Your loop executed 17 times instead of 15 because lack of synchronization. 由于缺少同步,循环执行了17次而不是15次。 Volatile guarantees only visibility, synchronization guarantees visibility and mutual exclusion. 挥发保证仅可见性,同步保证可见性和相互排斥。 As you are doing count++, which is compound action (read-increment-write), should use synchronization and as AdamSkywalker suggested AtomicInteger is your friend in this scenario. 在执行count ++(这是复合操作(读-增量-写))时,应使用同步,并且正如AdamSkywalker所建议的那样,在这种情况下AtomicInteger是您的朋友。

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

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