JUC详解
一、Java多线程 -- JUC包源码分析1 -- CAS/乐观锁
乐观锁其实就是不加锁,用CAS + 循环重试,实现多个线程/多个客户端,并发修改数据的问题
使用AtomicStampedReference类下的
public boolean compareAndSet(V expectedReference, //旧值 V newReference, //新值 int expectedStamp, //旧版本号 int newStamp //新版本号)
方法可实现原子性的CAS
–上面的atomicRef.compareAndSet(..)的第一个参数,传入的是一个ReferenceIntegerPair对象,它里面包含了2个字段:值 + 版本号。这也就意味着,它同时比较了值和版本号。
– 值不等,则肯定被其他线程改过了,不用再比较版本号,cas提交失败;
值相等,再比较版本号,如果版本号也相等,则说明真的没有被改过,cas提交成功;
值相等,版本号不等,则就是出现了ABA,cas提交失败。
二、Java多线程 -- JUC包源码分析2 -- Copy On Write/CopyOnWriteArrayList/CopyOnWriteArraySet
CopyOnWrite,
顾名思义:就是在Write的时候,不直接Write源数据,而是把数据Copy一份出来改,改完之后,再通过悲观锁或者乐观锁的方式写回去。
CopyOnWrite的主要目的是实现“写”加锁,“读”不加锁,从而提高并发度。在上面例子中,CopyOnWriteArrayList使用了CopyOnWrite + 悲观锁; NumberRange使用了CopyOnWrite + 乐观锁。
CopyOnWriteArraySet就是用Array实现的一个Set,保证所有元素都不重复。其内部就是封装的一个CopyOnWriteArrayList
三、Java多线程 -- JUC包源码分析3-- volatile/final语义
-volatile应用1 – 内存可见性 – JMM内存模型
线程A,线程B有各自的local内存。在把变量从主内存读到自己的工作内存,修改之后,不一定会立即写入主存,因此另一个线程不可见。
要保证上述案例可以完全正确执行,需要在变量前加volatile。
volatile变量可以保证:每次对该变量的写,必定刷回到主存;每次对该变量的读,必定从主存读取。从而可以保证,一个线程对共享变量的写,对其他线程可见。
-volatile应用2 – 原子性
由于JMM并不要求对一个64位的long/double型的变量写入具有原子性,在32位的机器上,对一个long型变量的写入,可能会分成高32位,低32位2次写入。此时,另一个线程去读取时,可能读到“写了一半”的无效值!
要解决上述问题,可以加锁,也可以加volatile关键字。
可见,在对单个变量的读写中,volatile变量起到了锁同样的作用。
也正因为如此,在AtomicInteger/AtomicLong中,其get()/set()函数,都未加锁,却是线程安全的!!
-volatile应用3 – 构造函数逸出/DCL问题(Double Checking Locking)
线程安全的单例模式中,有一种经典写法,即DCL(Doule Checking Locking),如下所示:
public class Sington
{
private static Sington instance;
public static Sington getInstance()
{
if(instance == null) //DCL
{
synchronized(Sington.class)
{
if(instance == null)
instance = new Instance(); //有问题的代码!!!
}
}
return instance;
}
上述的new Instance(),底层可以分为3个操作: 分配内存,在内存上初始化成员变量,把instance指向内存。
这3个操作,可能重排序,即先把instance指向内存,再初始化成员变量。
此时,另外一个线程就会拿到一个未完全初始化的对象。这时直接访问里面的成员变量,就可能出错。而这就是典型的“构造函数溢出”问题。
要解决此问题,只要在instance前加volatile就可以了!
当然,还有另外1种经典的线程安全的单例模式 – 基于类加载器的方案
public class Instance
{
private static class InstanceHolder
{
public static Instance instance = new Instance();
}
public static Instance getInstance()
{
return InstanceHolder.instance;
}
}
-final应用1 – 避免构造函数重排序
答案是:a, b 未必一定等于1,2。因为这里的i, j都是非volatile变量,线程A的重排序,可能使得i, j的赋值,在构造函数之后执行!!也就是说,线程B拿到obj的时候,obj的i, j变量可能赋值还未完成!
解决办法是:给i, j 加上final,final的语义: 保证final变量的初始化,一定在构造函数返回之前完成!
-final应用2 – CopyOnWrite
在上1篇 NumberRange例子中,我们看到lower, power都是final类型,这也确保了lower, power只可能被赋值1次。后续要想再改变值,只能拷贝一份出来改!
所以,通常应用CopyOnWrite的地方,也会相应的使用final!
-atomic数组/volatile数组/final数组/
-指令重排序,happen before语义
从上述各种案例可以看出,问题主要出在“指令重排序”上。
为什么要指令重排序呢?
从程序员角度来讲,最好是不要有任何的指令重排,这样程序最容易理解;但从CPU和编译器角度,希望在不改变单线程程序语义的情况下,尽可能的重排序,最大程度的提高执行效率。
而对于多线程程序,因为重排序导致的线程之间的不同步,则由程序员自己处理!
volatile和final的底层原理,就是一定程度上禁止重排序,从而实现多线程程序的同步。
四、Java多线程 -- JUC包源码分析4 -- 各种锁与无锁
未看懂。。。。。。。。
五、Java多线程 -- JUC包源码分析5 -- Condition/ArrayBlockingQueue/LinkedBlockingQueue/Deque/PriorityBl
参考:http://blog.csdn.net/chunlongyu/article/category/6399716
JUC详解的更多相关文章
- JUC详解--【Foam番茄】
1.什么是JUC java.util 工具包 业务:普通的线程代码 Thread Runnable 没有返回值,效率相比于 Callable 相对较低! 2.线程和进程 进程:一个程序,QQ.exe ...
- 跟着阿里p7一起学java高并发 - 第19天:JUC中的Executor框架详解1,全面掌握java并发核心技术
这是java高并发系列第19篇文章. 本文主要内容 介绍Executor框架相关内容 介绍Executor 介绍ExecutorService 介绍线程池ThreadPoolExecutor及案例 介 ...
- java高并发系列 - 第20天:JUC中的Executor框架详解2之ExecutorCompletionService
这是java高并发系列第20篇文章. 本文内容 ExecutorCompletionService出现的背景 介绍CompletionService接口及常用的方法 介绍ExecutorComplet ...
- JUC中的AQS底层详细超详解
摘要:当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒? 本文分享自华为云社区<JUC中的AQS底层详细超详解,剖 ...
- (转)Java并发包基石-AQS详解
背景:之前在研究多线程的时候,模模糊糊知道AQS这个东西,但是对于其内部是如何实现,以及具体应用不是很理解,还自认为多线程已经学习的很到位了,贻笑大方. Java并发包基石-AQS详解Java并发包( ...
- 【1】AQS详解
概述: 它内部实现主要是状态变量state和一个FIFO队列来完成,同步队列的头结点是当前获取到同步状态的结点,获取同步状态state失败的线程,会被构造成一个结点加入到同步队列尾部(采用自旋CAS来 ...
- Disruptor 详解 二
Disruptor 的大名从很久以前就听说了,但是一直没有时间:看完以后才发现其内部的思想异常清晰,很容易就能前移到其他的项目,所以仔细了解一下还是很有必要的这.篇博客将主要从源码角度分析,Disru ...
- Mysql高手系列 - 第9篇:详解分组查询,mysql分组有大坑!
这是Mysql系列第9篇. 环境:mysql5.7.25,cmd命令中进行演示. 本篇内容 分组查询语法 聚合函数 单字段分组 多字段分组 分组前筛选数据 分组后筛选数据 where和having的区 ...
- 最强Java并发编程详解:知识点梳理,BAT面试题等
本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...
随机推荐
- 解决Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/cqupt/paging/dao/User.xml
搭建了一个ssm项目,启动报错Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find re ...
- the principle of redbalck tree
- C# ComboBoxTree控件
这个控件,百度.codeproject等,能查到更好的解答和代码.写这个,是因为,我在3个小时内,没有看懂接手项目,原有的TreeCombox自定义代码逻辑.而我觉得很有必要改动它.因为新弄得网页端很 ...
- 关于Jordan标准形
[转载请注明出处]http://www.cnblogs.com/mashiqi 2017/06/25 设$A$是$n$维线性空间$V$上的线性变换,它的特征值与相应的代数重数分别为$\lambda_i ...
- C#operator作用
opertor用于定义类型转化时采用两种方式,饮食转换implicit和显示转换explicit public static implicit 目标类型(被转化类型 变量参数) { return 目标 ...
- #电脑磁盘分区#新买的电脑一般只有C盘或者C盘和D盘,怎么加多几个盘呢
新买的电脑一般只有C盘或者C盘和D盘,怎么加多几个盘呢 鼠标右键点击桌面我的电脑选择管理 进入计算机管理.选择磁盘管理 若桌面没有我的电脑,可按win+x键,在快捷菜单栏中点击磁盘管理 通过以上两种w ...
- js封装选项卡
<div class="forestcamp_box"> <img src="img/home_02.jpg" /> <div c ...
- Java+opencv实现人脸检测
版本 Java1.8 opencv3.4 代码: import java.awt.Graphics; import java.awt.image.BufferedImage; import javax ...
- C++ Primer Plus (Stephen Prata 著)
第1章 预备知识 (已看) 第2章 开始学习C++ (已看) 第3章 处理数据 (已看) 第4章 复合类型 (已看) 第5章 循环和关系表达式 (已看) 第6章 分支语句和逻辑运算符 (已看) 第7章 ...
- 图像小波变换去噪——MATLAB实现
clear; [A,map]=imread('C:\Users\wangd\Documents\MATLAB\1.jpg'); X=rgb2gray(A); %画出原始图像 subplot(,,);i ...