Java JVM 之 Volatile

废话不说,直接上代码。

private Boolean flag = false;

private void test() throws InterruptedException {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread1 start.");
                
                while(!flag) {
                    
                }
                
                System.out.println("thread1 end");
            }
        });
        thread1.start();
        Thread.sleep(1000);
        
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread2 start");
                flag = true;
                System.out.println("thread2 end");
            }
        });
        thread2.start();
    }

上面的这段代码,执行结果是:

thread1 start.

thread2 start.

thread2 end

那么thread2里面对flag进行赋值,为什么thread1不会停止呢?

在jvm中,每个线程中都有一个相对应的栈,在thread1的run栈帧中,flag变量的值并不会随着thread2的run栈帧中的flag变量的变化而变化。

换句话说,这个flag变量,在内存中存在三个。即堆、run1栈帧、run2栈帧。

那么在flag变量的前面加上volatile关键字,就可以把thread1和thread2中的flag变量保持同步。

在volatile关键字声明的变量上,栈帧对静态变量表中的变量进行赋值时,会同时写入堆中变量。在这之前,会同时通知其他栈帧a,清除静态变量表中同一变量,在其他栈帧a使用时,会去堆中再次寻找该变量。这种方式也称作 “JMM 缓存一致性协议”。

所以volatile的第一个作用就是“线程可见性”。

在volatile声明的变量下,操作数栈中write该变量时,同步到堆中之前,会对其进行lock,也就是其他线程是无法read到变量的。在同步后解锁该变量。

volatile第二个作用是禁止指令重排。

当我们使用单例模式的时候,通常都是双重检查模式。thread1进来创建对象的时候,如果只初始化了一个对象引用,还没有赋值。thread2再来获取对象,检查不为null时,就会直接返回该对象,但是对象中的变量值还没有被赋值。

所以使用volatile可以避免出现这种问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值