java多线程之volatile

  在了解之前我们需要先了解一下Java内存模型(区别与JVM的内存模型)。

Java内存模型

  Java 内存模型来屏蔽掉各种硬件和操作系统的内存差异,达到跨平台的内存访问效果。JLS(Java语言规范)定义了一个统一的内存管理模型JMM(Java Memory Model)

  Java内存模型规定了所有的变量都存储在主内存中,此处的主内存仅仅是虚拟机内存的一部分,而虚拟机内存也仅仅是计算机物理内存的一部分(为虚拟机进程分配的那一部分)。

  Java内存模型分为主内存和工作内存。主内存是所有的线程所共享的,工作内存是每个线程自己有一个,不是共享的。

  每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的(未使用到的不拷贝)变量的主内存副本拷贝。线程对变量的所有操作(读取、赋值),都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

Java内存间交互操作

  JLS定义了线程对主存的操作指令:lock,unlock,read,load,use,assign,store,write。这些行为是不可分解的原子操作,在使用上相互依赖,read-load从主内存复制变量到当前工作内存,use-assign执行代码改变共享变量值,store-write用工作内存数据刷新主存相关内容。

  • read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用;

  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中;

  • use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作;

  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;

  • store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作;

  • write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中;

Java并发编程之可见性

  可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

举个简单的例子,看下面这段代码:

//线程1执行的代码
int i = 0;
i = 10;

//线程2执行的代码
j = i;

  由上面的分析可知,当线程1执行 i =10这句时,会先把i的初始值加载到线程1的工作内存中,然后赋值为10,那么在线程1的工作内存当中i的值变为10了,却没有立即写入到主存当中。

此时线程2执行 j = i,它会先去主存读取i的值并加载到线程2的工作内存当中,注意此时主存当中i的值还是0,那么就会使得j的值为0,而不是10。

这就是可见性问题,线程1对变量i修改了之后,线程2没有立即看到线程1修改的值。

volatile的作用

  对于可见性,Java提供了volatile关键字来保证可见性。

  当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

  而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

  另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

代码

package com.xiayc.vol;

public class VolatileTester extends Thread {
    private boolean arg1 = false;
    private volatile boolean arg2 = false;

    public void setArg1(boolean arg1) {
        this.arg1 = arg1;
    }

    public void setArg2(boolean arg2) {
        this.arg2 = arg2;
    }

    @Override
    public void run() {
        System.out.println("线程开始。。。");
        while(true) {
            if(this.arg1) {
                System.out.println("arg1改变了");
                break;
            }
            if(this.arg2) {
                System.out.println("arg2改变了");
                break;
            }
        }
        System.out.println("线程结束。。。");
    }

    public static void main(String[] args) throws InterruptedException {
        VolatileTester volatileTester = new VolatileTester();
        volatileTester.start();

        Thread.sleep(200);

        volatileTester.setArg1(true);
        volatileTester.setArg2(true);
    }
}

执行结果

这段代码的执行结果一定是
线程开始。。。
arg1改变了
线程结束。。。

吗?不一定,也许在大多数时候,这个代码的运行结果是这样,但是也有可能是
线程开始。。。
arg2改变了
线程结束。。。

虽然这个可能性很小,但是只要一旦发生这种情况就会造成死锁。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值