一、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. Vagrant 创建虚拟环境

    1. 添加box vagrant box add --name centos6.8/cms boxname.box 2.初始化admin环境 vagrant init centos6.8/cms ad ...

  2. 洛谷P1019:单词接龙(DFS)

    题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的"龙"(每个单词都最多在"龙" ...

  3. 开源WHMCS支付宝当面付和即时到账插件

    开源WHMCS支付宝当面付和即时到账插件 链接: https://pan.baidu.com/s/1i5HU4hn 密码: crq7

  4. Java 的 volatile 修饰符

    volatile 修饰符,用于多线程同步 volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值.而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存.这 ...

  5. 2.5 Visio2007不规则图形填充

    1.确保线和线接口的地方正好相交,没有多出来的线: 2.将图形选中>组合: 3.选中图形>形状>操作>连接>填充颜色. 因为图形式几条线段拼合的,不是封闭图形,所以需要将 ...

  6. docker push到私有仓库

    1.登录 docker login http://xxxxx.com 2.登录私有hub创建项目 例如项目叫:abc-dev 2.给镜像打tag docker tag 2e25d8496557 xxx ...

  7. 【剑指offer】广度优先遍历二叉树

    问题:从上往下打印出二叉树的每个节点,同层节点从左至右打印. *思路:先用队列存放树的根结点.每次出队一个结点,将结点非空的左右孩子分别入队.重复此过程,直到队列为空. import java.uti ...

  8. Hadoop 管理工具HUE配置-Hive配置

    1 前言 首先要配置好Hive,可以参见:http://www.cnblogs.com/liuchangchun/p/4761730.html 2 hive配置 找到beeswax标签,不叫hive, ...

  9. ubuntu16.04 安装最新版nodejs

    ubuntu软件仓库中自带的nodejs版本过低 $ apt-cache policy nodejs nodejs: Installed: (none) Candidate: 4.2.6~dfsg-1 ...

  10. 采用ddt 可以把ddt获取的数据 塞进测试用例里面的备注里面去展示 (还没有试)