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关键字只能解决线程的可见性问题,不能解决线程的原子性问题

源码地址

(Java多线程系列七)Java内存模型和线程的三大特性的更多相关文章

  1. java多线程系列(七)---Callable、Future和FutureTask

    Callable.Future和FutureTask 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量 ...

  2. 深入理解Java虚拟机读书笔记8----Java内存模型与线程

    八 Java内存模型与线程   1 Java内存模型     ---主要目标:定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节.     ---此处的变量和J ...

  3. 【java多线程系列】java内存模型与指令重排序

    在多线程编程中,需要处理两个最核心的问题,线程之间如何通信及线程之间如何同步,线程之间通信指的是线程之间通过何种机制交换信息,同步指的是如何控制不同线程之间操作发生的相对顺序.很多读者可能会说这还不简 ...

  4. 【java多线程系列】java中的volatile的内存语义

    在java的多线程编程中,synchronized和volatile都扮演着重要的 角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性,可见性指的是当一 ...

  5. Java多线程系列七——ExecutorService

    java.util.concurrent.ExecutorService接口提供了许多线程管理的方法 Method 说明 shutdown 拒绝接收新的任务,待已提交的任务执行后关闭,且宿主线程不阻塞 ...

  6. 【Java多线程系列七】ExecutorService

    java.util.concurrent.ExecutorService接口提供了许多线程管理的方法 Method 说明 shutdown 拒绝接收新的任务,待已提交的任务执行后关闭,且宿主线程不阻塞 ...

  7. 深入理解java虚拟机(6)---内存模型与线程 & Volatile

    其实关于线程的使用,之前已经写过博客讲解过这部分的内容: http://www.cnblogs.com/deman/category/621531.html JVM里面关于多线程的部分,主要是多线程是 ...

  8. Java多线程系列一——Java实现线程方法

    Java实现线程的两种方法 继承Thread类 实现Runnable接口 它们之间的区别如下: 1)Java的类为单继承,但可以实现多个接口,因此Runnable可能在某些场景比Thread更适用2) ...

  9. java多线程系列(八)---CountDownLatch和CyclicBarrie

    CountDownLatch 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线 ...

随机推荐

  1. MySQL 获取本月第一天、下个月第一天等

    select curdate(); --获取当前日期 select last_day(curdate()); --获取当月最后一天. select DATE_ADD(curdate(),interva ...

  2. html常用代码

    <marquee width="70%" scrollamount="2">大家好</marquee>    // 大家好 字符从左到右 ...

  3. delphi 在代码中 添加 TO-DO 并且 管理

    TO-DO List是一项非常好用的功能.采用她可以让我们很清楚的了解以前完成了那些任务,还有哪些任务需要做,由谁负责完成,是不是比较紧急的任务等.今天来不及完成的,明天上班就可以很快的找到任务所在的 ...

  4. fabric && cita 调研对比

    fabric && cita 调研 总结 若计划完全依赖上游的基础功能而不做任何改造,建议选择 fabric:否则,应选择 cita,针对自身业务场景进行持续优化. 一.功能 1.可扩 ...

  5. js日历算法

    页面 <div class="un1"> <h2>服务档期</h2> <div class="date-panel" ...

  6. JNDI数据源的配置及使用

    数据源的作用JDBC操作的步骤:  1. 加载驱动程序  2. 连接数据库  3. 操作数据库  4. 关闭数据库,释放连接 也就是说,所有的用户都需要经过此四步进行操作,但是这四步之中有三步对所有人 ...

  7. C++中函数调用操作符的重载

    1,本博文讲述函数对象问题: 2,客户需求: 1,编写一个函数: 1,函数可以获得斐波那契数列每项的值: 2,每调用一次返回一个值: 3,函数可根据需要重复使用: 4,代码示例: ; i<; i ...

  8. 【学习总结】Python-3-多个变量赋值

    菜鸟教程-Python3-基本数据类型 同时为多个变量赋值的两种格式: 连等:看起来可能错误但事实上Python可以这样的.... 一团变量对应一团值:比较常见又省事的格式 END

  9. HTML页面仿iphone数字角标

    做仿iphone样式的数字角标,用简单的css来实现 <html><head><title>角标数字</title><style type=&qu ...

  10. WPFの触发器详解

    例子1 简单触发器Triggers——满足简答的条件,触发 <Window x:Class="Styles.SimpleTriggers" xmlns="http: ...