一、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详解的更多相关文章

  1. JUC详解--【Foam番茄】

    1.什么是JUC java.util 工具包 业务:普通的线程代码 Thread Runnable 没有返回值,效率相比于 Callable 相对较低! 2.线程和进程 进程:一个程序,QQ.exe ...

  2. 跟着阿里p7一起学java高并发 - 第19天:JUC中的Executor框架详解1,全面掌握java并发核心技术

    这是java高并发系列第19篇文章. 本文主要内容 介绍Executor框架相关内容 介绍Executor 介绍ExecutorService 介绍线程池ThreadPoolExecutor及案例 介 ...

  3. java高并发系列 - 第20天:JUC中的Executor框架详解2之ExecutorCompletionService

    这是java高并发系列第20篇文章. 本文内容 ExecutorCompletionService出现的背景 介绍CompletionService接口及常用的方法 介绍ExecutorComplet ...

  4. JUC中的AQS底层详细超详解

    摘要:当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒? 本文分享自华为云社区<JUC中的AQS底层详细超详解,剖 ...

  5. (转)Java并发包基石-AQS详解

    背景:之前在研究多线程的时候,模模糊糊知道AQS这个东西,但是对于其内部是如何实现,以及具体应用不是很理解,还自认为多线程已经学习的很到位了,贻笑大方. Java并发包基石-AQS详解Java并发包( ...

  6. 【1】AQS详解

    概述: 它内部实现主要是状态变量state和一个FIFO队列来完成,同步队列的头结点是当前获取到同步状态的结点,获取同步状态state失败的线程,会被构造成一个结点加入到同步队列尾部(采用自旋CAS来 ...

  7. Disruptor 详解 二

    Disruptor 的大名从很久以前就听说了,但是一直没有时间:看完以后才发现其内部的思想异常清晰,很容易就能前移到其他的项目,所以仔细了解一下还是很有必要的这.篇博客将主要从源码角度分析,Disru ...

  8. Mysql高手系列 - 第9篇:详解分组查询,mysql分组有大坑!

    这是Mysql系列第9篇. 环境:mysql5.7.25,cmd命令中进行演示. 本篇内容 分组查询语法 聚合函数 单字段分组 多字段分组 分组前筛选数据 分组后筛选数据 where和having的区 ...

  9. 最强Java并发编程详解:知识点梳理,BAT面试题等

    本文原创更多内容可以参考: Java 全栈知识体系.如需转载请说明原处. 知识体系系统性梳理 Java 并发之基础 A. Java进阶 - Java 并发之基础:首先全局的了解并发的知识体系,同时了解 ...

随机推荐

  1. 解决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 ...

  2. the principle of redbalck tree

  3. C# ComboBoxTree控件

    这个控件,百度.codeproject等,能查到更好的解答和代码.写这个,是因为,我在3个小时内,没有看懂接手项目,原有的TreeCombox自定义代码逻辑.而我觉得很有必要改动它.因为新弄得网页端很 ...

  4. 关于Jordan标准形

    [转载请注明出处]http://www.cnblogs.com/mashiqi 2017/06/25 设$A$是$n$维线性空间$V$上的线性变换,它的特征值与相应的代数重数分别为$\lambda_i ...

  5. C#operator作用

    opertor用于定义类型转化时采用两种方式,饮食转换implicit和显示转换explicit public static implicit 目标类型(被转化类型 变量参数) { return 目标 ...

  6. #电脑磁盘分区#新买的电脑一般只有C盘或者C盘和D盘,怎么加多几个盘呢

    新买的电脑一般只有C盘或者C盘和D盘,怎么加多几个盘呢 鼠标右键点击桌面我的电脑选择管理 进入计算机管理.选择磁盘管理 若桌面没有我的电脑,可按win+x键,在快捷菜单栏中点击磁盘管理 通过以上两种w ...

  7. js封装选项卡

    <div class="forestcamp_box"> <img src="img/home_02.jpg" /> <div c ...

  8. Java+opencv实现人脸检测

    版本 Java1.8 opencv3.4 代码: import java.awt.Graphics; import java.awt.image.BufferedImage; import javax ...

  9. C++ Primer Plus (Stephen Prata 著)

    第1章 预备知识 (已看) 第2章 开始学习C++ (已看) 第3章 处理数据 (已看) 第4章 复合类型 (已看) 第5章 循环和关系表达式 (已看) 第6章 分支语句和逻辑运算符 (已看) 第7章 ...

  10. 图像小波变换去噪——MATLAB实现

    clear; [A,map]=imread('C:\Users\wangd\Documents\MATLAB\1.jpg'); X=rgb2gray(A); %画出原始图像 subplot(,,);i ...