并发编程-volatile的可见性

下面的代码,修改load函数中doSomething(0);传入的值,从0到4,分别会出现能跳出循环和不能跳出循环两种情况
0,什么也不做,不能跳出循环
1,sleep,能跳出循环
2,使用System.out.println输出,能跳出循环
3,等待10微秒,不能跳出循环
4,等待20微秒,能跳出循环

package thread;
public class VisibilityTest {
    
  private boolean flag = true;

  public void refresh() {
    flag = false;
    System.out.println(Thread.currentThread().getName() + "修改flag");
 }


  public void load() {
    System.out.println(Thread.currentThread().getName() + "开始执行...");
    int i = 0;
    while (flag) {
        i ++;
        doSomething(0);
    }
    System.out.println(Thread.currentThread().getName() + "跳出循环:i=" + i);
 }


  public void doSomething(int x) {
    switch (x) {
        case 1: // 能跳出循环
            try {
                Thread.sleep(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            break;
        case 2: // 能跳出循环
            System.out.println("====="); // synchronized
            break;
        case 3: // 不能跳出循环
            shortWait(10000); // 10微秒
            break;
        case 4: // 能跳出循环
            shortWait(20000); // 20微秒
            break;
        case 0: // 不能跳出循环
            default: // 不能跳出循环// do nothingbreak;
    }
}


public static void shortWait(long interval) {
    long start = System.nanoTime();
    long end;
    do {
        end = System.nanoTime();
    } while (start + interval >= end);
}


public static void main(String[] args) {
    VisibilityTest test = new VisibilityTest();
    new Thread(() -> test.load(), "threadA").start();
    try {
        Thread.sleep(2000);
        new Thread(() -> test.refresh(), "threadB").start();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

对于ThreadA,会经过read、load操作将flag读入工作内存(本地内存),然后use操作写入cpu core1(寄存器保存);
对于ThreadB,也会有上面的步骤。但最后是写入cpu core2(因为cpu core1一直在被占用),然后给flag赋值为false,再经过assign操作写回工作内存,注意,此时不会立马执行store、write操作,写回主内存,而是在某一时刻进行;
此时,ThreadA继续读取flag,是从工作内存中加载的,不是从主内存中加载的load函数中doSomething(0);传入的值,从0到4:
• 0,什么也不做,while(flag)一直在进行,threadA的工作内存缓存一直有效,因此一直未从主内存中读取,一直是true;
• 1,sleep,会让出CPU时间片,线程上下文切换(保存现场、恢复现场),因此会从主内存中重新读取flag,读到了false则跳出循环;
注意:与sleep的时间没有关系,即时sleep0ms,也会让出CPU时间片;
• 2,使用System.out.println输出,println方法实现内部有synchronized(this)操作,synchronized会保证可见性,因此会从主内存中读取flag。读到了false则跳出循环;
• 3,等待10微秒,太短了,缓存未失效,因此一直未从主内存中读取,一直是true;
• 4,等到20微秒,缓存失效了,因此会从主内存中读取,读到了false则跳出循环;
以上问题归根到底是可见性问题。当线程2更改了主内存的值后,没有及时的让线程1知道这个值被更改了。

如果给flag变量加上volatile关键词后:
private volatile boolean flag = true;
上述0-4,执行都会跳出循环。
上面例子表示了volatile关键字的含义之一:保证变量对所有线程的可见性(及时性,一但线程1更改了变量,线程2立马知道。底层是通过缓存一致性协议。)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@来杯咖啡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值