Java多线程 线程安全之volatile

Java线程安全主要是由两个特性来组成,一、原子性。二、可见性。java

 

1.volatile关键字

原子性相似于数据库事务中的原子性,一个操做必须善始善终,不能中途被中止。数据库

而可见性的意思:是多个线程之间访问共享变量时,A线程所修改的变量须要及时的被B线程或其余全部线程所读取到安全

直接上代码,下面先看看当一个典型的多线程同步时的问题。多线程

public class RunThread implements Runnable {
    private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

    @Override
    public void run() {
        System.out.println("进入run了");
        while (isRunning) {

        }
        System.out.println("线程被中止了!");
    }

    public static void main(String[] args) throws InterruptedException {
        RunThread thread = new RunThread();
        new Thread(thread).start();
        Thread.sleep(100);
        thread.setRunning(false);
        System.out.println("已经赋值为false");
    }
}

输出结果:ide

根据代码,咱们在main线程中已经设置isRunning为false,子线程thread应该跳出while的死循环,打印出“线程被中止了!”,而结果为何没有呢?this

这就引出了JMM 内存模型的主存和内存的关系问题,先看下JMM 内存模型的示意图。spa

这里写图片描述

能够看到,每一个线程都拥有一个独立的本地内存(也能够叫工做内存),而下面大的主内存则是由全部线程所共享的,其实每次线程中所读取到的变量都是从主存中得来的,而后写入到本身的工做内存而后进行操做线程

基于这个模型,咱们其实就能够回答上面的问题,在子线程中咱们每次读取isRunning,都是从本地内存中,全部取得值一直是false,而在主线程中咱们设置了isRunning为true是在主存中,子线程中没有及时的读取到主线程中改变的isRunning变量。这就形成了一个线程之间共享变量没有及时同步,也能够说是一个线程改变的值,没有及时被其余线程所看到。这里咱们就引入了volatile关键字,只要被它修饰的变量在进行修改时,会向总线(BUS)发送消息,其余线程读取到该变量时,会获得最新的值。简单的来讲,若是想要某个变量被全部线程读取到最新的值,就使用volatile关键字进行修饰。code

因此在上面,咱们仅仅须要在isRunning前面加上volatile关键字修饰,以下:blog

private volatile boolean isRunning = true;

输出结果:

能够看到,程序已经如咱们所预期的,达到线程安全的效果了。

2.volatile的局限性

其实volatile关键字解决的仅仅是线程之间可见性的问题,换句话说,它仅仅的解决的是线程之间数据可否被读取到,而没有解决多线程的原子性问题,即读到的数据是否是一个线程完整执行后的结果值。若是想保证多线程的原子性问题,能够参考java下的concurrent包下提供了一些原子类,如tomicInteger、AtomicLong、AtomicReference等,能够很好的解决变量数据的正确性问题。

相关文章
相关标签/搜索