话说Volatile关键字

一、保持多线程环境中共享变量的可见性。

       在多线程环境下,每个线程都有一个自己的线程栈,对于共享的变量(比如堆中new的变量),每个线程栈都会拷贝一个该变量的副本,使用volatile修饰的变量,当线程修改当前栈程栈的副本时,修改后的副本数据会马上更新到共享变量上。

       如下图所示,我们把变量data用volatile修饰,然后启动两个线程分别对data进行自增操作,在初始化的步骤中,线程1和线程2分别拷贝一份变量的副本到两个线程的栈中,两个线程内部分别进行循环,每循环一次data就自增一次。而因为data被volatile关键字修饰,则每自增一次,线程栈中的data变量副本都会强制更新到共享变量data中。同时,其它线程中的data副本将被置为无效,其它线程在使用data时会从新从堆中读取。

       下面的代码,就是图中线程执行函数的例子:

public class MyRunnable implements Runnable {
    public static Integer data = 0;
    private Integer spon = 0;
    @Override
    public void run() {
        try {
            while (true) {
                data++;
                Thread.sleep(1000 * (spon / 2 + 1));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

需要注意的是:

(1)volatile关键字可以强制程序将线程栈中的副本刷新的共享变量,但不是说:如果一个共享变量没有被voltile修饰,它的副本在线程中被修改时就不会马上更新到共享变量中。实际上这种情况很少会发生。

(2)因为int等基本类型在深拷贝时才具有原子性,volatile关键字一般只用来修饰基本的数据类型。

(3)volatile关键字并不能代替synchronized等锁进行线程间的同步作用,比如在上述的代码中,data的自增操作不具有原子性,它实际分为两个过程,一是告诉缓存的数据进行自增操作,二是自增后的数据更新到内存。

二、禁止编译器的指令重排功能

      在默认情况下,java编译器具有指令重拍功能,比如以下代码:

int a=1;
int b=2;
int c=a+b;

       编译器在进行编译时,可能a=1在前面,也可能b=2在前面。但是并不是所有的代码都会被编译器指令重拍,比如下面的代码:

int a=1;
a=a+1;
system.out.print(a);

       这种情况下,第2行的代码直接影响到了第3行的打印结果,编译器肯定不会进行指令重排。

       在多线程的条件下,指令重拍可能会影响到执行执行的结果,比如下面的代码:

共享变量:

boolean flag=false;
int data=1;

线程1:

if(flag=-true){
    system.out.println(data);
}

线程2:

data++;
flag=true;

        显然线程2的两条指令如果重排将会直接影响线程1的输出结果,此时我们可以用volatile关键字修饰上述两个变量,这样禁止了编译器的指令重排。volatile在禁止指令重拍方面应用最广的应该算是在单例模式上,如下代码:

 public class MyObj {
    private static volatile MyObj instance = new MyObj();
    public static MyObj getInstance() {
        return instance;
    }
}

        在执行MyObj instance = new MyObj();时实际上被分成了三个步骤:

  • 1、在栈中创建一片内存给instance变量。
  • 2、在堆中new一个MyObj对象。
  • 3、将步骤2中的对象指向步骤1中的变量。

        在多线程环境下,如果编译器进行了指令重排,将步骤2和步骤3对调,那么我们在使用MyObj的单例对象时很可能会出现null指针异常。所以最安全的方法就是将instance对象用volatile关键字进行修饰,禁止编译器指令重排。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值