Java内存模型三大特性
文章目录
- 一、原子性
- 如何保证原子性?
- 1、使用原子类来保证线程安全
- 2、使用synchronized 互斥锁
- 二、可见性
- 如何保证可见性?
- 三、有序性
- 如何保证有序性?
- 1、volatile关键字
- 2、synchronized关键字
一、原子性
Java 内存模型保证了 read、load、use、assign、store、write、lock 和 unlock 操作具有原子性,例如对一个 int 类型的变量执行 assign 赋值操作,这个操作就是原子性的。但是 Java 内存模型允许虚拟机将没有被 volatile 修饰的 64 位数据(long,double)的读写操作划分为两次 32 位的操作来进行,即 load、store、read 和 write 操作可以不具备原子性。
有一个错误认识就是,int 等原子性的类型在多线程环境中不会出现线程安全问题。前面的线程不安全示例代码中,cnt 属于 int 类型变量,1000 个线程对它进行自增操作之后,得到的值为 997 而不是 1000。
为了方便讨论,将内存间的交互操作简化为 3 个:load、assign、store。
下图演示了两个线程同时对 cnt 进行操作,load、assign、store 这一系列操作整体上看不具备原子性,那么在 T1 修改 cnt 并且还没有将修改后的值写入主内存,T2 依然可以读入旧值。可以看出,这两个线程虽然执行了两次自增运算,但是主内存中 cnt 的值最后为 1 而不是 2。因此对 int 类型读写操作满足原子性只是说明 load、assign、store 这些单个操作具备原子性。
如何保证原子性?
1、使用原子类来保证线程安全
原子类位于 java.util.concurrent.atmoic 包,这里以AtomicInteger类为例:
AtomicInteger 能保证多个线程修改的原子性。
使用 AtomicInteger 重写之前线程不安全的代码之后得到以下线程安全实现:
public class AtomicExample {private AtomicInteger cnt = new AtomicInteger();public void add() {cnt.incrementAndGet();}public int get() {return cnt.get();}
}
public static void main(String[] args) throws InterruptedException {final int threadSize = 1000;AtomicExample example = new AtomicExample(); // 只修改这条语句final CountDownLatch countDownLatch = new CountDownLatch(threadSize);ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i < threadSize; i++) {executorService.execute(() -> {example.add();countDownLatch.countDown();});}countDownLatch.await();executorService.shutdown();System.out.println(example.get());
}
输出
1000
2、使用synchronized 互斥锁
它对应的内存间交互操作为:lock 和 unlock,在虚拟机实现上对应的字节码指令为 monitorenter 和 monitorexit。
public class AtomicSynchronizedExample {private int cnt = 0;public synchronized void add() {cnt++;}public synchronized int get() {return cnt;}
}
public static void main(String[] args) throws InterruptedException {final int threadSize = 1000;AtomicSynchronizedExample example = new AtomicSynchronizedExample();final CountDownLatch countDownLatch = new CountDownLatch(threadSize);ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i < threadSize; i++) {executorService.execute(() -> {example.add();countDownLatch.countDown();});}countDownLatch.await();executorService.shutdown();System.out.println(example.get());
}
输出
1000
二、可见性
可见性指当一个线程修改了共享变量的值,其它线程能够立即得知这个修改。Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。
如何保证可见性?
- volatile
- synchronized,对一个变量执行 unlock 操作之前,必须把变量值同步回主内存。
- final,被 final 关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。
对前面的线程不安全示例中的 cnt 变量使用 volatile 修饰,不能解决线程不安全问题,因为 volatile 并不能保证操作的原子性。
三、有序性
有序性是指:在本线程内观察,所有操作都是有序的。在一个线程观察另一个线程,所有操作都是无序的,无序是因为发生了指令重排序。在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
如何保证有序性?
1、volatile关键字
volatile 关键字通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前。
2、synchronized关键字
也可以通过 synchronized 来保证有序性,它保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码。
Java内存模型三大特性相关推荐
- Java内存模型及三大特性
在了解Java的内存模型之前先了解下计算机处理并发的模型处理: 由于计算的处理器的处理速度与存储设备的读写速度的差异较大,所以加入一层读写接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之 ...
- Java并发知识梳理(上):并发优缺点,线程状态转换,Java内存模型,Synchronized,Volatile,final,并发三特性,Lock与AQS,ReetrandLock
努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! 整个系列文章为Java并发专题,一是自己的兴趣,二是,这部分在实际理解上很有难度,另外在面试过程中也是经常被问到.所以在学习过程中,记 ...
- java(线程特性,Volatile作用,java内存模型)
1.java线程特性 1.原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行 银行转账,自己转100给别人,自己账户少100 别人多100 不会出现,自己 ...
- 并发编程专题——第一章(深入理解java内存模型)
说到并发编程,其实有时候觉得,开发中真遇到这些所谓的并发编程,场景多吗,这应该是很多互联网的在职人员,一直在考虑的事情,也一直很想问,但是又不敢问,想学习的同时,网上这些讲的又是乱七八糟,那么本章开始 ...
- 并发编程之Java内存模型
在介绍Java内存模型之前,先来了解一下为什么要有内存模型,以及内存模型是什么.然后我们基于对内存模型的了解,学习Java内存模型以及并发编程的三大特性. 为什么要有内存模型 在计算机中,所有的运算操 ...
- Java多线程编程笔记4:Java内存模型
Java内存模型 Java内存模型试图屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果. 处理器上的寄存器读写速度比内存快几个数量级,为了解决这种速度矛 ...
- java内存模型按照线程隔离性_深入理解Java多线程与并发框(第③篇)——Java内存模型与原子性、可见性、有序性...
一.Java内存模型 Java Memory Modle,简称 JMM,中文名称 Java内存模型,它是一个抽象的概念,用来描述或者规范访问内存变量的方式.因为各中计算机的操作系统和硬件不同,方式机制 ...
- JAVA 内存模型 (Java Memory Model,JMM)
JAVA内存模型 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. Java内存模型(Java Memory Model,JMM) 是在硬件内存模型基 ...
- JAVA内存模型与线程安全
什么是线程安全?在<<深入理解Java虚拟机>>中看到的定义.原文如下:当多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替运行,也不需要进行额外的同步, ...
最新文章
- LAMP服务搭建详解
- echo -e 参数
- bootstrap的滚动监听
- 2019-11-08 频域的直观感受
- php把数字倒着展示,jQuery+PHP实现动态数字展示特效
- java api es_ES 常用java api
- 2016年安防企业如何因势而动 顺势而为
- [jquery] 图片热区随图片大小自由缩放
- C#——NotifyICON的使用
- 国内智能工厂建设现状以及未来发展趋势介绍
- html的vr图怎么制作,什么叫vr全景展示?vr图片怎么做的?
- 广州图书馆——携程评论爬取
- 惠普刀片服务器型号,HP ProLiant刀片服务器简介
- Facade与Adapter模式应用
- 结构化思维模型的构建(多种思维参考)
- python||报错‘gbk‘ codec can‘t decode byte 0x80 in position 8: illegal multibyte sequence
- 极验验证的滑动验证码破解
- 中电药明招募资深Python开发工程师
- 锁系列:一、悲观 / 乐观锁原理与运用
- python会搞坏电脑吗_Python 操作几个坏习惯,你中了吗?
热门文章
- Python 的PIL,可以解决ImportError The _imagingft C module is not installed
- 【BZOJ】【3301】【USACO2011 Feb】Cow Line
- 向窗口发送ctrl c复制 消息
- C# 特性(Attribute)学习
- Symbian c++ 在3版中实现并动态配置开机自启动
- 《设计模式详解》行为型模式 - 解释器模式
- Linux系统管理(8)——Ubuntu安装ssh服务 以及版本查看命令
- DBSCAN聚类算法初探(五)
- 谁说数据分析很难?看完这7大分析套路后,还学不会的来找我
- 数据可视化必备的高逼格图表特效,学会只需要五分钟