多线程和并发(五)volatile关键字

一,什么是volatile?

很多人都听说过volatile,估计用过的人远没有听过的人多。volatile和synchronized(https://mp.csdn.net/postedit/81675479)一样,都是Java用来实现同步机制的一种手段,当变量声明为volatile时,代表这个变量是共享的,当前线程对它进行的操作都会立即对其它线程可见,也就是保证了可见性,既然提到了可见性,就来说说Java并发编程的三大概念:

 1.原子性一个操作或者一轮操作要么全部进行,要么全部不进行

    比如说:i=i+1事实上并不是一个一个操作,在进行i=i+1时先从内存中取i的数值,然后进行加一操作,也就是说有两个操作,并不     是一步完成,而像i=10赋值操作就是一步完成,具备原子性

2.可见性:是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是说如果一个线程进行修改操作a=10任意的      操作都行,那么其他线程访问a时结果就是10

3.有序性:就是程序执行的顺序按照代码的先后顺序执行,这个很好理解,但是为什么要特意提出这个概念呢,是因为JVM执行时会进行指令重排序,指令重排序是CPU为了提高运行效率,会对代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。

二,volatile常见问题

1,关于可见性

一个变量被volatile修饰,不仅保证了可见性,同时禁止指令重排序,来看一段很经典的代码:

//线程1
boolean stop = false;
while(!stop){
    do();
}
 
//线程2
stop = true;

这段代码用来中断线程,当stop为false时线程1循环,等到线程2修改游标stop时,线程1会因为stop更改为true而中断退出,然而中断效果没有想象中的那么完美,实际运行时会进入死循环,,这是由于线程对变量进行操作时,会先复制一个副本,修改副本的值再存回内存,一旦线程2获取了副本修改之后没有来得及存储进主存线程1就得到了副本,此时stop的值被线程2修改了但是没有更新,所以值对线程1没变,于是无法中断线程,进入死循环。这才是可见性的重要性。

2.能否实现原子性

虽然volatile保证了可见性,但是并没有保证原子性,这个和保证可见性并不矛盾,来看下面这段代码

public class VolatileTest {
    public volatile int number= 0;
     
    public void add() {
        number=number+1;
    }
     
    public static void main(String[] args) {
        final VolatileTest test = new VolatileTest ();
        for(int i=0;i<3;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<10;j++)
                        test.add();
                };
            }.start();
        }
    }
        System.out.println(test.number);
    }
}

最后输出结果并不是大家想像中的3*10=30,结果是不是唯一的,原因在于number=number+1不是一个原子性操作,当执行完读取number的值步骤后,其他线程完成了对number的更新,则结果就会发生变化

3. 能否保证有序性

由于保证了可见性,则也能保证有序性。禁止指令重排序会使得将线程不能对被volatile修饰的变量进行操作顺序的改变,保证volatile发挥可见性的作用,也会保证有序性。

三.volatile的经典应用

1.作为状态标记量(可以理解为游标),用来中断线程

public volatile boolean flag = true;
 //线程1
while(flag){
    doSomething();
}
  //线程2
public void setFlag() {
    flag = false;
}

2.Double-Check(单例模式中的双重检查,在项目开发中会经常看到这种代码)

public class Test{
    private volatile static Test instance = null;
 
    private Test() {
 
    }
 
    public static Test getInstance() {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance = new Test();
            }
        }
        return instance;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_38162448/article/details/81813885
今日推荐