(Java多线程系列七)Java内存模型和线程的三大特性

Java内存模型和线程的三大特性

多线程有三大特性:原子性、可见性、有序性

1、Java内存模型

Java内存模型(Java Memory Model ,JMM),决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。

用一张图表示Java内存模型

2、原子性

原子性即一个操作或多个操作,要么全部执行并且执行过程不被任何因素打断,要么就都不执行。

一个经典的例子就是数据库存储的事务。原子性其实就是保证数据一致、线程安全的一部分。

Synchronized、lock可以解决线程原子性问题

3、可见性

当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改其他线程没看到,这就是可见性问题。

Volatile可以解决线程可见性问题

4、有序性

程序执行的顺序按照代码的先后顺序执行。

一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。

而在多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

5、volatile关键字可以解决线程之间可见性的问题

class ThreadDemo extends Thread {

    boolean flag;

    ThreadDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        System.out.println(getName() + "线程开始运行。。。");
        while (flag) {
        }
        System.out.println(getName() + "线程已经结束。。。");
    }

    public void stopThread() {
        this.flag = false;
    }
}

public class VolatileThreadDemo {

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo(true);
        threadDemo.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadDemo.stopThread();
    }
}

先来看以上的代码,在主线程中调用threadDemo.stopThread()方法停止线程,看上去逻辑没有问题,但是我们会发现线程没有停止。

注意:有的同学可能在测试上面代码的时候程序可以正常退出。那是因为你的JVM没有优化造成的!

造成线程没有停止的原因是while(flag)中的flag是在线程运行的“工作内存”中获取的,而不是从“主内存”中获取的,这就造成了我们在主线程中改变flag的值对于子线程中不生效。只要在flag前加volatile关键字,强制线程每次读取该值的时候都去“主内存”中取值,就能解决我们的问题。

package com.littlestones.volatiledemo;

/**
 * @program: JavaThreadLearn
 * @description: volatile示例
 * @author: Leil
 * @create: 2019-12-24 15:22
 */

class ThreadDemo extends Thread {

    volatile boolean flag;

    ThreadDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        System.out.println(getName() + "线程开始运行。。。");
        while (flag) {
        }
        System.out.println(getName() + "线程已经结束。。。");
    }

    public void stopThread() {
        this.flag = false;
    }
}

public class VolatileThreadDemo {

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo(true);
        threadDemo.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadDemo.stopThread();
    }
}

注意:volatile关键字只能解决线程的可见性问题,不能解决线程的原子性问题

源码地址

posted @ 2019-12-25 15:13  3LittleStones  阅读(510)  评论(0编辑  收藏  举报