Java并发系列:volatile 线程间可见性、非原子性特性

/ JavaSE / 442浏览

线程间可见性

被声明volatile关键字的变量,在多线程之间保证了变量值可见的特性,适当的使用volatile关键字,可以减少锁的使用,提升性能。

用例代码:

package cn.zealon.volatiles;

/**
 * volatile特性 - 线程间共享变量
 */
public class UseVolatile implements Runnable {
    //保证了stop变量的多线程之间可见性
    private volatile boolean stop = true;

    public boolean getStop() {
        return stop;
    }

    public void setStop(boolean stop) {
        this.stop = stop;
    }

    @Override
    public void run() {
        while (stop==true) {
            System.out.println("stop:" + stop);
        }
        System.out.println("线程终止了。");
    }

    public static void main(String[] args){
        UseVolatile uv = new UseVolatile();
        Thread t1 = new Thread(uv);
        t1.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        uv.setStop(false);
        System.out.println("stop 设置了false");
    }
}

代码中线程他t1执行后,主线程休眠2秒,然后调用了赋值函数,这时由于stop是被volatile修饰的,线程t1获取最新的stop变量的值由false变成true,所以代码执行会退出的死循环,最后线程t1也终止了。

非原子性

使用volatile关键字,需要注意重要的一点,被volatile声明的变量非原子性的。 我们例举个场景: 假设在代码中使用10个线程,同时调用一个方法对静态变量num累加操作,每次累加100;那么结果当10个线程运行结束后,num的值应该为1000。但是由于volatile关键字不是具有原子性,num的最终结果可能不是1000。

用例代码:

package cn.zealon.volatiles;

/**
 * volatile特性 - 非原子性
 */
public class UseVolatile2 implements Runnable{

    private static volatile int num =0;

    public static void addNum(){
        for (int i=0;i<100;i++){
            num++;
        }
        System.out.println("num:"+num);
    }

    @Override
    public void run() {
        addNum();
    }

    public static void main(String[] args){
        UseVolatile2[] useVolatile2 =new UseVolatile2[10];

        for(int i=0;i<10;i++){
            useVolatile2[i]=new UseVolatile2();
        }

        for (int i=0;i<useVolatile2.length;i++){
            new Thread(useVolatile2[i]).start();
        }
    }
}

执行结果可能每次都不一样,可以多运行几次:

num:200
num:200
num:300
num:463
num:463
num:563
num:663
num:763
num:863
num:963