java多线程之volatile关键字

public class ThreadVolatile extends Thread {
    public boolean flag=true;

    @Override
    public void run() {
        System.out.println("子线程begin。。。。。。");
        while(flag){}
        System.out.println("子线程end。。。。。");
    }

    public void setFlag(boolean flag){
        this.flag=flag;
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadVolatile threadVolatile=new ThreadVolatile();
        threadVolatile.start();
        Thread.sleep(3000);//作用是不立刻通知子线程、有延迟
        threadVolatile.setFlag(false);
        System.out.println("已经修改为false");//其实修改的是主内存里面的值,而threadVolatile中依然是副本也就是true
        Thread.sleep(1000);
        System.out.println(threadVolatile.flag);
    }
}

运行上述代码输出如下:

子线程begin。。。。。。
           已经修改为false
           false

????发现并没有输出System.out.println("子线程end。。。。。");也就是说该线程并没有停止,程序一直running。

原因:线程之间是不可见的,读取的是副本,没有及时读取到主内存结果。主线程main修改了自己本地私有内存中flag的值为false并且刷新到主内存中,但是threadVolatile并没有及时从主内存中读取值而是读取本地私有内存,自然而然flag值为true,导致子线程threadVolatile一致无法停止

解决办法:使用Volatile关键字将解决线程之间可见性, 强制线程每次读取该值的时候都去“主内存”中取值

将上述public boolean flag=true;修改为public volatile boolean  flag=true;后即可得到如下输出结果:

子线程begin。。。。。。
           已经修改为false
           子线程end。。。。。
            false

注意: Volatile非原子性

public class VolatileNoAtomic extends Thread {
    //多个线程同时共享,static修饰关键字,存放在静态区,只会存放一次
    private volatile static int count=0;
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            count++;

        }
        System.out.println(getName()+","+count);
    }

    public static void main(String[] args) {
        VolatileNoAtomic[] volatileNoAtomics=new VolatileNoAtomic[10];
        for (int i = 0; i < volatileNoAtomics.length; i++) {
            volatileNoAtomics[i]=new VolatileNoAtomic();
        }
        for (int i = 0; i < volatileNoAtomics.length; i++) {
            volatileNoAtomics[i].start();
        }

    }
}

程序输出如下结果:

Thread-1,1504
Thread-0,1504
Thread-2,2504
Thread-3,3504
Thread-4,4504
Thread-5,5504
Thread-6,6504
Thread-7,7504
Thread-8,8504
Thread-9,9504

平均运行10次左右会得到以上结果。数据不同步,这就说明了volatile不具备原子性。

public class VolatileNoAtomic extends Thread {
    //多个线程同时共享,static修饰关键字,存放在静态区,只会存放一次
//    private volatile static int count=0;
    private static AtomicInteger count=new AtomicInteger(0);
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
//            count++;

            count.incrementAndGet();
        }
        System.out.println(getName()+","+count.get());
    }

    public static void main(String[] args) {
        VolatileNoAtomic[] volatileNoAtomics=new VolatileNoAtomic[10];
        for (int i = 0; i < volatileNoAtomics.length; i++) {
            volatileNoAtomics[i]=new VolatileNoAtomic();
        }
        for (int i = 0; i < volatileNoAtomics.length; i++) {
            volatileNoAtomics[i].start();
        }

    }
}

输出可能如下:线程间可见,有的还在++,有的直接System.out.println,但总会有10000

Thread-1,2000
Thread-0,2000
Thread-2,3000
Thread-3,4000
Thread-4,5000
Thread-5,6000
Thread-7,7000
Thread-8,8000
Thread-9,9000
Thread-6,10000

volatile与synchronized区别

仅靠volatile不能保证线程的安全性。(原子性)

volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法

volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。

synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。

线程安全性

线程安全性包括两个方面,可见性。原子性。

从上面自增的例子中可以看出:仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。

猜你喜欢

转载自blog.csdn.net/qq_37049496/article/details/81173044