Java并发--三大性质
一、多线程的三大性质
原子性;可见性、有序性
二、原子性
原子性介绍
原子性是指:一个操作时不可能中断的,要么全部执行成功要么全部执行失败,有着同生共死的感觉。即使在多线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。
先看看哪些是原子操作,哪些不是原子操作:
int a=10; // a++; // int b=a; // a=a+1; //
上面这四个语句中只有第1个语句是原子操作,将10赋值给线程工作内存的变量a,而语句2 a++,实际上包含了三个操作:读取变量a的值;对进行加1的操作,将计算后的值在赋值给变量a,而这三个操作都无法构成原子操作。对语句3,4的分析同理,这两条语句不具备原子性。当然,Java内存模型中定义了8中操作都是原子的,不可再分的。
lock(锁定):作用于主内存中的变量,它把一个变量标示为一个线程独占的状态;
unlock(解锁):作用于主内存中的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存,以便后面的load动作使用;
load(载入):作用于工作内存中的变量,它把read操作从主内存中得到的变量值放入工作内存中的变量副本;
use(使用):作用于工作内存中的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值得字节码指令时将会执行这个操作;
assign(赋值):作用于工作内存中的变量,它把一个执行引擎接受到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传递给主内存中以便随后的write操作使用;
write(操作):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。
上面的这些操作时相当底层的。那么如何理解这些指令呢?比如:把一个变量从主内存复制到工作内存中就需要执行read,load操作,将工作内存同步到主内存中就需要执行store,write操作。注意的是:Java内存模型只是要求上述两个操作时顺序执行的并不是连续执行的。也就是说read和load之间可以插入其他指令,store和write也可以插入其他指令。比如对主内存中的a,b进行访问就可以出现这样的操作顺序:read a,read b,load b,load a
由原子性操作变量read,load,use,assign,store,write可以大致认为基本数据类型的访问读写具备原子性(例外的就是long和double的非原子性协定)
三、synchronized和volatile的原子性
synchronized
上面一共有八条原子操作,其中六条可以满足基本数据类型的访问读写具备原子性,还剩下lock和unlock两条原子操作。
如果我们需要大范围的原子操作就可以使用lock和unlock原子操作。尽管JVM没有吧lock和unlock开放给我们,但JVM以更高层次的指令monitorenter和monitorexit开放给我们使用,反映到Java代码中就是---synchronized关键字,也就是说synchronized满足原子性.
volatile
package passtra;
public class VolatileExample{
private static volatile int count=0;
public static void main(String[] args) {
for(int i=0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10000;i++){
count++;
}
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println(count);
}
}
开启10个线程,每个线程都自加10000次,如果不出现线程安全的问题最终的结果应该是10*10000,可是运行多次都是小于100000。
问题在于volatile并不能保证原子性,count++并不是一个院子操作,包含了三个步骤:1、读取变量count的值;2、对count加1;3、将新值赋给变量count。如果线程A读取count到工作内存中,其他线程对这个值已经做了自增操作后,那么线程A的值自然而然就是一个过期的值,因此,总结过必然会是小于100000的。
如果让volatile保证原子性,就必须符合以下两个原则:
运算结果并不依赖变量的当前的值,或者能够确保只有一个线程修改变量的值;
变量不需要与其他的状态变量共同参与不变约束。
四、synchronized和volatile的有序性
synchronized
synchronized语义表示锁在同一时刻只能由一个线程进行获取,当锁被占用后,其他线程只能等待。
因此,synchronized语义就要求线程在访问读写操作共享变量时只能串行执行,因此synchronized具有有序性。
volatile
在JMM 中,为了性能优化,编译器和处理器会进行指令重排序,也就是说Java程序天然的有序性,可以总结为:如果在本线程内观察,所有的操作都是有序的,如果在一个线程观察另一个线程,所有的操作都是无序的。
在单例模式的实现上有一种双重检验锁定的方式DCL(Double-checked Locking)
public class Singleton{
private static volatile Singleton singleton;
private Singleton(){}
public static Singleton getsingleton(){
if(singleton==null){
synchronized (Singleton.class) {
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
这里为什么要加volatile?先分析下不加volatile的情况,有问题的语句是这条:singleton=new Singleton();这条语句实际上包含了三个操作:1、分配对象的内存空间;2、初始化对象;3、设置singleton指向刚分配的内存地址。但由于存在重排序的问题,可能有以下的执行顺序:如果2和3进行了重排序的话,线程B进行判断if(singleton==null)时就会出现true,而实际撒花姑娘这个singleto并没有初始化成功,显而易见对B线程来说之后的操作就会是错的。
而用volatile修饰的话就可以禁止2和3操重排序,从而避免这种情况
volatile包含禁止指令重排序的语义,其具有有序性。
五、synchronized和volatile的可见性
可见性是指:当一个线程修改了共享变量后,其他线程能够立即得知这个修改。
synchronized:当线程获取锁时或从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。所以,synchronized具有可见性
volatile:同样在volatile分析中,会通过在指令中天机lock指令,一实现内存可见性,因此,volatile具有可见性
所以:synchronized具有原子性,有序性和可见性
volatile具有有序性和可见性
Java并发--三大性质的更多相关文章
- Java三大性质总结:原子性、可见性以及有序性
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...
- java并发 - 自底向上的原理分析
[TOC] 事先声明,我只是java并发的新手,这篇文章也只是我阅读<java并发编程的艺术>一书(内容主要涉及前3章)的一些总结和感悟.希望大家能多多讨论,对于错误的地方还请指出. 0. ...
- 面渣逆袭:Java并发六十问,快来看看你会多少道!
大家好,我是老三,面渣逆袭 继续,这节我们来盘一盘另一个面试必问知识点--Java并发. 这篇文章有点长,四万字,图文详解六十道Java并发面试题.人已经肝麻了,大家可以点赞.收藏慢慢看!扶我起来,我 ...
- 多线程的通信和同步(Java并发编程的艺术--笔记)
1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递. 2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...
- Java并发编程:同步容器
Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...
- 《Java并发编程实战》第十一章 性能与可伸缩性 读书笔记
造成开销的操作包含: 1. 线程之间的协调(比如:锁.触发信号以及内存同步等) 2. 添加�的上下文切换 3. 线程的创建和销毁 4. 线程的调度 一.对性能的思考 1 性能与可伸缩性 执行速度涉及下 ...
- java并发面试
1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...
- Java并发编程75道面试题及答案
1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...
- 《Java并发编程的艺术》Java并发机制的底层实现原理(二)
Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...
随机推荐
- Java 继承与抽象类
一.继承 1.概念 在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类,子类会自动拥有父类所有可继承的属性和方法. 2.继承的格式&使用 ...
- phpcms视频模块实现列表页打开内容页直接播放视频
摘自phpcms论坛 原链接地址:http://bbs.phpcms.cn/thread-557691-1-1.html 之前下载研究过“化蝶自在飞”开发的视频模型,发现功能不错,但唯一的缺憾是,我想 ...
- SpringMV学习之拦截器的简单使用
一.拦截器介绍 我们知道SpringMVC的拦截器(Interceptor)非常重要,尤其在Web应用中使用非常广泛,其功能类似于过滤器,用于拦截用户请求并作相应的处理.我们使用比较多的场景是系统登陆 ...
- Skill 返回list中的倒数第二个数据
https://www.cnblogs.com/yeungchie/ code procedure(ycLast2(list) prog((index ouput) unless(type(list) ...
- 51nod 1584 加权约数和 约数和函数小trick 莫比乌斯反演
LINK:加权约数和 我曾经一度认为莫比乌斯反演都是板子题. 做过这道题我认输了 不是什么东西都是板子. 一个trick 设\(s(x)\)为x的约数和函数. 有 \(s(i\cdot j)=\sum ...
- .NetCore 入门
.net core是什么? .net core是一个可以用来构建现代.可伸缩和高性能的跨平台软件应用程序的通用开发框架. 我们为什么要使用.net core,也就是说.net core有什么好处? 跨 ...
- 题解 [NOI2015]程序自动分析
据说考前写题解可以$\text{RP}$++? 这题还是算一道并查集水题了吧qwq我又做了好久 ---------------------------------------------------- ...
- 基于 JavaEmail 简单的发送邮件点到点,一对多(图片和附件)之多收件人,多少送人
if(!StringUtil.isEmpty(message_type_to)){ if (message_type_to.contains(",")) { String[] sp ...
- three.js 着色器材质之glsl内置函数
郭先生发现在开始学习three.js着色器材质时,我们经常会无从下手,辛苦写下的着色器,也会因莫名的报错而手足无措.原因是着色器材质它涉及到另一种语言–GLSL,只有懂了这个语言,我们才能更好的写出着 ...
- 大型企业都在用的Python反爬虫手段,破了它!
SVG 映射反爬虫 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人 ...