(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关键字只能解决线程的可见性问题,不能解决线程的原子性问题
(Java多线程系列七)Java内存模型和线程的三大特性的更多相关文章
- java多线程系列(七)---Callable、Future和FutureTask
Callable.Future和FutureTask 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量 ...
- 深入理解Java虚拟机读书笔记8----Java内存模型与线程
八 Java内存模型与线程 1 Java内存模型 ---主要目标:定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节. ---此处的变量和J ...
- 【java多线程系列】java内存模型与指令重排序
在多线程编程中,需要处理两个最核心的问题,线程之间如何通信及线程之间如何同步,线程之间通信指的是线程之间通过何种机制交换信息,同步指的是如何控制不同线程之间操作发生的相对顺序.很多读者可能会说这还不简 ...
- 【java多线程系列】java中的volatile的内存语义
在java的多线程编程中,synchronized和volatile都扮演着重要的 角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性,可见性指的是当一 ...
- Java多线程系列七——ExecutorService
java.util.concurrent.ExecutorService接口提供了许多线程管理的方法 Method 说明 shutdown 拒绝接收新的任务,待已提交的任务执行后关闭,且宿主线程不阻塞 ...
- 【Java多线程系列七】ExecutorService
java.util.concurrent.ExecutorService接口提供了许多线程管理的方法 Method 说明 shutdown 拒绝接收新的任务,待已提交的任务执行后关闭,且宿主线程不阻塞 ...
- 深入理解java虚拟机(6)---内存模型与线程 & Volatile
其实关于线程的使用,之前已经写过博客讲解过这部分的内容: http://www.cnblogs.com/deman/category/621531.html JVM里面关于多线程的部分,主要是多线程是 ...
- Java多线程系列一——Java实现线程方法
Java实现线程的两种方法 继承Thread类 实现Runnable接口 它们之间的区别如下: 1)Java的类为单继承,但可以实现多个接口,因此Runnable可能在某些场景比Thread更适用2) ...
- java多线程系列(八)---CountDownLatch和CyclicBarrie
CountDownLatch 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线 ...
随机推荐
- Windows-计划任务-自动备份数据库和文件
开始 -> 程序 -> 附件 -> 系统工具 -> 计划任务 .bat 文件如下: ::数据库+文件备份 @echo off ::日期时间 set yyyymmdd=%date ...
- springboot 应用程序的文件检索描述
SpringApplication从application.properties以下位置的文件加载属性并将它们添加到Spring Environment: 一个/config当前目录下的子目录. 当前 ...
- Windwos 08R2_DNS+AD安装图文
目录 目录 前言 软件环境 DNS域名服务器 配置DNS服务器 AD活动目录 配置AD域 前言 Windows 2008 R2平台下搭建AD(Active Directory)和DNS(Domain ...
- ASP.NET开发知识总结
1.统一异常处理 某商城采用的异常处理方式,是全局统一捕捉,统一处理 思路: 一.定义异常过滤器 实现 MyExceptionFilter : FilterAttribute,IExceptio ...
- multiprocessing的Process类的简单使用
''' 跨平台的进程创建模块(multiprocessing) 支持跨平台 :window/linux multiprocessing提供一个Process类来代表一个进程对象 ''' from mu ...
- js设置cookies
//写入cookies的方法 function setCookie(name, value, seconds) { seconds = seconds || 0; //seconds有值就直接赋值,没 ...
- MFC坐标问题
页面空间中的矩形被称为窗口,设备空间中的矩形被称为视口. 页面空间与设备空间的转换示意图: 页面空间到设备空间的转换需要两个矩形的宽高比(转换因子). 设备空间到物理空间转换的唯一作用是平移,并由Wi ...
- C++中的智能指针类模板
1,智能指针本质上是一个对象,这个对象可以像原生的指针一样使用,因为智能指 针相关的类通过重载的技术将指针相关的操作符都进行了重载,所以智能指针对象可以像原生指针一样操作,今天学习智能指针类模板,通过 ...
- maven上传源码脚本
mvn deploy:deploy-file -Dmaven.test.skip=true -Dfile=./target/bbc-common-1.0.0-source.jar -DgroupId= ...
- automapper实体中的映射和聚合根中的使用
一,如下例子: using AutoMapper; using System; using System.Collections.Generic; using System.Linq; using S ...