JUC (Java Util Concurrency) 基础内容概述
目录[-]
转自:http://www.goldendoc.org/2011/05/juc/
1. JUC概况
以下是Java JUC包的主体结构:
- Atomic : AtomicInteger
- Locks : Lock, Condition, ReadWriteLock
- Collections : Queue, ConcurrentMap
- Executer : Future, Callable, Executor
- Tools : CountDownLatch, CyclicBarrier, Semaphore
2. 原子操作
多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤,那么这个操作就是原子的。出现原因: synchronized的代价比较高。
以下以AtomicInteger为例:
- int addAndGet(int delta):以原子方式将给定值与当前值相加。 实际上就是等于线程安全版本的i =i+delta操作。
- boolean compareAndSet(int expect, int update):如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。 如果成功就返回true,否则返回false,并且不修改原值。
- int decrementAndGet():以原子方式将当前值减 1。 相当于线程安全版本的–i操作。
- int getAndAdd(int delta):以原子方式将给定值与当前值相加。 相当于线程安全版本的t=i;i+=delta;return t;操作。
- int getAndDecrement():以原子方式将当前值减 1。 相当于线程安全版本的i–操作。
- int getAndIncrement():以原子方式将当前值加 1。 相当于线程安全版本的i++操作。
- int getAndSet(int newValue):以原子方式设置为给定值,并返回旧值。 相当于线程安全版本的t=i;i=newValue;return t;操作。
- int incrementAndGet():以原子方式将当前值加 1。 相当于线程安全版本的++i操作。
3. 指令重排
你的程序并不能总是保证符合CPU处理的特性。
要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。

多核CPU,大压力下,两个线程交替执行,x,y输出结果不确定。可能结果:
|
1
2
3
4
|
x =0, y =1
x =1, y =1
x =1, y =0
x =0, y =0
|
4. Happens-before法则:(Java 内存模型)
如果动作B要看到动作A的执行结果(无论A/B是否在同一个线程里面执行),那么A/B就需要满足happens-before关系。
Happens-before的几个规则:
- Program order rule:同一个线程中的每个Action都happens-before于出现在其后的任何一个Action。
- Monitor lock rule:对一个监视器的解锁happens-before于每一个后续对同一个监视器的加锁。
- Volatile variable rule:对volatile字段的写入操作happens-before于每一个后续的同一个字段的读操作。
- Thread start rule:Thread.start()的调用会happens-before于启动线程里面的动作。
- Thread termination rule:Thread中的所有动作都happens-before于其他线程检查到此线程结束或者Thread.join()中返回或者Thread.isAlive()==false。
- Interruption rule:一个线程A调用另一个另一个线程B的interrupt()都happens-before于线程A发现B被A中断(B抛出异常或者A检测到B的isInterrupted()或者interrupted())。
- Finalizer rule:一个对象构造函数的结束happens-before与该对象的finalizer的开始
- Transitivity:如果A动作happens-before于B动作,而B动作happens-before与C动作,那么A动作happens-before于C动作。
因为CPU是可以不按我们写代码的顺序执行内存的存取过程的,也就是指令会乱序或并行运行, 只有上面的happens-before所规定的情况下,才保证顺序性。
JMM的特性:
多个CPU之间的缓存也不保证实时同步;
JMM不保证创建过程的原子性,读写并发时,可能看到不完整的对象。(so D-check)
volatile语义:
volatile实现了类似synchronized的语义,却又没有锁机制。它确保对 volatile字段的更新以可预见的方式告知其他的线程。
- Java 存储模型不会对volatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。
- volatile变量不会被缓存在寄存器中(只有拥有线程可见),每次总是从主存中读取volatile变量的结果。
ps:volatile并不能保证线程安全的,也就是说volatile字段的操作不是原子性的,volatile变量只能保证可见性。
5. CAS操作
Compare and Swap
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
实现简单的非阻塞算法:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
privatevolatileintvalue;// 借助volatile原语,保证线程间的数据是可见的
publicfinalintget() {
returnvalue;
}
publicfinalintincrementAndGet() {
for(;;) {
intcurrent = get();
intnext = current +1;
if(compareAndSet(current, next))
returnnext;
}//Spin自旋等待直到返为止置
}
|
整个J.U.C都是建立在CAS之上的,对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。会出现所谓的“ABA”问题
6. Lock 锁
Synchronized属于独占锁,高并发时性能不高,JDK5以后开始用JNI实现更高效的锁操作。
Lock—->
ReentrantLock—->
ReentrantReadWriteLock.ReadLock / ReentrantReadWriteLock.writeLock
ReadWriteLock—-> ReentrantReadWriteLock
LockSupport
Condition
| 方法名称 | 作用 |
| void lock() | 获取锁。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。 |
| void lockInterruptibly() throws InterruptedException; | 如果当前线程未被中断,则获取锁。如果锁可用,则获取锁,并立即返回。 |
| Condition newCondition(); |
返回绑定到此 Lock 实例的新 Condition 实例 |
| boolean tryLock(); | 仅在调用时锁为空闲状态才获取该锁 |
| boolean tryLock(long time, TimeUnit unit) throws InterruptedException; | 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁 |
| void unlock(); | 释放锁 |
PS : 一般来说,获取锁和释放锁是成对儿的操作,这样可以避免死锁和资源的浪费。
注:在 finally 里面做释放锁的操作
7. AQS
锁机制实现的核心所在。AbstractQueuedSynchronizer是Lock/Executor实现的前提。

AQS实现:
基本的思想是表现为一个同步器,AQS支持下面两个操作:
acquire:
|
1
2
3
4
5
|
while(synchronization state does not allow acquire){
enqueue current threadifnot already queued;
possibly block current thread;
}
dequeue current threadifit was queued;
|
release:
|
1
2
3
|
update synchronization state;
if(state may permit a blocked thread to acquire)
unlock one or more queued threads;
|
要支持这两个操作,需要实现的三个条件:
- Atomically managing synchronization state(原子性操作同步器的状态位)
- Blocking and unblocking threads(阻塞和唤醒线程)
- Maintaining queues(维护一个有序的队列)
Atomically managing synchronization state
使用一个32位整数来描述状态位:private volatile int state; 对其进行CAS操作,确保值的正确性。
Blocking and unblocking threads
JDK 5.0以后利用JNI在LockSupport类中实现了线程的阻塞和唤醒。
LockSupport.park() //在当前线程中调用,导致线程阻塞
LockSupport.park(Object)
LockSupport.unpark(Thread)
Maintaining queues
在AQS中采用CHL列表来解决有序的队列的问题。(CHL= Craig, Landin, and Hagersten)

Node里面是什么结构?

WaitStatus –>节点的等待状态,一个节点可能位于以下几种状态:
- CANCELLED = 1: 节点操作因为超时或者对应的线程被interrupt。节点不应该不留在此状态,一旦达到此状态将从CHL队列中踢出。
- SIGNAL = -1: 节点的继任节点是(或者将要成为)BLOCKED状态(例如通过LockSupport.park()操作),因此一个节点一旦被释放(解锁)或者取消就需要唤醒(LockSupport.unpack())它的继任节点。
- CONDITION = -2:表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。
- 0: 正常状态,新生的非CONDITION节点都是此状态。
非负值标识节点不需要被通知(唤醒)。
队列管理操作:
入队enqueue:
采用CAS操作,每次比较尾结点是否一致,然后插入的到尾结点中。
|
1
2
3
|
do{
pred = tail;
}while( !compareAndSet(pred,tail,node) );
|
出队dequeue:
|
1
2
|
while(pred.status != RELEASED) ;
head = node;
|

加锁操作:
|
1
2
3
4
5
|
public final void acquire(intarg) {
if(!tryAcquire(arg))
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
|
释放操作:
|
1
2
3
4
5
6
7
8
9
|
public final boolean release(intarg) {
if(tryRelease(arg)) {
Node h = head;
if(h !=null && h.waitStatus !=0)
unparkSuccessor(h);
return true;
}
return false;
}
|
The synchronizer framework provides a ConditionObject class for use by synchronizers that maintain exclusivesynchronization and conform to the Lock interface. —— Doug Lea《 The java.util.concurrent Synchronizer Framework 》
以下是AQS队列和Condition队列的出入结点的示意图,可以通过这几张图看出线程结点在两个队列中的出入关系和条件。




0赞目录[-]
转自:http://www.goldendoc.org/2011/05/juc/
1. JUC概况
以下是Java JUC包的主体结构:
- Atomic : AtomicInteger
- Locks : Lock, Condition, ReadWriteLock
- Collections : Queue, ConcurrentMap
- Executer : Future, Callable, Executor
- Tools : CountDownLatch, CyclicBarrier, Semaphore
2. 原子操作
多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤,那么这个操作就是原子的。出现原因: synchronized的代价比较高。
以下以AtomicInteger为例:
- int addAndGet(int delta):以原子方式将给定值与当前值相加。 实际上就是等于线程安全版本的i =i+delta操作。
- boolean compareAndSet(int expect, int update):如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。 如果成功就返回true,否则返回false,并且不修改原值。
- int decrementAndGet():以原子方式将当前值减 1。 相当于线程安全版本的–i操作。
- int getAndAdd(int delta):以原子方式将给定值与当前值相加。 相当于线程安全版本的t=i;i+=delta;return t;操作。
- int getAndDecrement():以原子方式将当前值减 1。 相当于线程安全版本的i–操作。
- int getAndIncrement():以原子方式将当前值加 1。 相当于线程安全版本的i++操作。
- int getAndSet(int newValue):以原子方式设置为给定值,并返回旧值。 相当于线程安全版本的t=i;i=newValue;return t;操作。
- int incrementAndGet():以原子方式将当前值加 1。 相当于线程安全版本的++i操作。
3. 指令重排
你的程序并不能总是保证符合CPU处理的特性。
要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。

多核CPU,大压力下,两个线程交替执行,x,y输出结果不确定。可能结果:
|
1
2
3
4
|
x =0, y =1
x =1, y =1
x =1, y =0
x =0, y =0
|
4. Happens-before法则:(Java 内存模型)
如果动作B要看到动作A的执行结果(无论A/B是否在同一个线程里面执行),那么A/B就需要满足happens-before关系。
Happens-before的几个规则:
- Program order rule:同一个线程中的每个Action都happens-before于出现在其后的任何一个Action。
- Monitor lock rule:对一个监视器的解锁happens-before于每一个后续对同一个监视器的加锁。
- Volatile variable rule:对volatile字段的写入操作happens-before于每一个后续的同一个字段的读操作。
- Thread start rule:Thread.start()的调用会happens-before于启动线程里面的动作。
- Thread termination rule:Thread中的所有动作都happens-before于其他线程检查到此线程结束或者Thread.join()中返回或者Thread.isAlive()==false。
- Interruption rule:一个线程A调用另一个另一个线程B的interrupt()都happens-before于线程A发现B被A中断(B抛出异常或者A检测到B的isInterrupted()或者interrupted())。
- Finalizer rule:一个对象构造函数的结束happens-before与该对象的finalizer的开始
- Transitivity:如果A动作happens-before于B动作,而B动作happens-before与C动作,那么A动作happens-before于C动作。
因为CPU是可以不按我们写代码的顺序执行内存的存取过程的,也就是指令会乱序或并行运行, 只有上面的happens-before所规定的情况下,才保证顺序性。
JMM的特性:
多个CPU之间的缓存也不保证实时同步;
JMM不保证创建过程的原子性,读写并发时,可能看到不完整的对象。(so D-check)
volatile语义:
volatile实现了类似synchronized的语义,却又没有锁机制。它确保对 volatile字段的更新以可预见的方式告知其他的线程。
- Java 存储模型不会对volatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。
- volatile变量不会被缓存在寄存器中(只有拥有线程可见),每次总是从主存中读取volatile变量的结果。
ps:volatile并不能保证线程安全的,也就是说volatile字段的操作不是原子性的,volatile变量只能保证可见性。
5. CAS操作
Compare and Swap
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
实现简单的非阻塞算法:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
privatevolatileintvalue;// 借助volatile原语,保证线程间的数据是可见的
publicfinalintget() {
returnvalue;
}
publicfinalintincrementAndGet() {
for(;;) {
intcurrent = get();
intnext = current +1;
if(compareAndSet(current, next))
returnnext;
}//Spin自旋等待直到返为止置
}
|
整个J.U.C都是建立在CAS之上的,对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。会出现所谓的“ABA”问题
6. Lock 锁
Synchronized属于独占锁,高并发时性能不高,JDK5以后开始用JNI实现更高效的锁操作。
Lock—->
ReentrantLock—->
ReentrantReadWriteLock.ReadLock / ReentrantReadWriteLock.writeLock
ReadWriteLock—-> ReentrantReadWriteLock
LockSupport
Condition
| 方法名称 | 作用 |
| void lock() | 获取锁。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。 |
| void lockInterruptibly() throws InterruptedException; | 如果当前线程未被中断,则获取锁。如果锁可用,则获取锁,并立即返回。 |
| Condition newCondition(); |
返回绑定到此 Lock 实例的新 Condition 实例 |
| boolean tryLock(); | 仅在调用时锁为空闲状态才获取该锁 |
| boolean tryLock(long time, TimeUnit unit) throws InterruptedException; | 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁 |
| void unlock(); | 释放锁 |
PS : 一般来说,获取锁和释放锁是成对儿的操作,这样可以避免死锁和资源的浪费。
注:在 finally 里面做释放锁的操作
7. AQS
锁机制实现的核心所在。AbstractQueuedSynchronizer是Lock/Executor实现的前提。

AQS实现:
基本的思想是表现为一个同步器,AQS支持下面两个操作:
acquire:
|
1
2
3
4
5
|
while(synchronization state does not allow acquire){
enqueue current threadifnot already queued;
possibly block current thread;
}
dequeue current threadifit was queued;
|
release:
|
1
2
3
|
update synchronization state;
if(state may permit a blocked thread to acquire)
unlock one or more queued threads;
|
要支持这两个操作,需要实现的三个条件:
- Atomically managing synchronization state(原子性操作同步器的状态位)
- Blocking and unblocking threads(阻塞和唤醒线程)
- Maintaining queues(维护一个有序的队列)
Atomically managing synchronization state
使用一个32位整数来描述状态位:private volatile int state; 对其进行CAS操作,确保值的正确性。
Blocking and unblocking threads
JDK 5.0以后利用JNI在LockSupport类中实现了线程的阻塞和唤醒。
LockSupport.park() //在当前线程中调用,导致线程阻塞
LockSupport.park(Object)
LockSupport.unpark(Thread)
Maintaining queues
在AQS中采用CHL列表来解决有序的队列的问题。(CHL= Craig, Landin, and Hagersten)

Node里面是什么结构?

WaitStatus –>节点的等待状态,一个节点可能位于以下几种状态:
- CANCELLED = 1: 节点操作因为超时或者对应的线程被interrupt。节点不应该不留在此状态,一旦达到此状态将从CHL队列中踢出。
- SIGNAL = -1: 节点的继任节点是(或者将要成为)BLOCKED状态(例如通过LockSupport.park()操作),因此一个节点一旦被释放(解锁)或者取消就需要唤醒(LockSupport.unpack())它的继任节点。
- CONDITION = -2:表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。
- 0: 正常状态,新生的非CONDITION节点都是此状态。
非负值标识节点不需要被通知(唤醒)。
队列管理操作:
入队enqueue:
采用CAS操作,每次比较尾结点是否一致,然后插入的到尾结点中。
|
1
2
3
|
do{
pred = tail;
}while( !compareAndSet(pred,tail,node) );
|
出队dequeue:
|
1
2
|
while(pred.status != RELEASED) ;
head = node;
|

加锁操作:
|
1
2
3
4
5
|
public final void acquire(intarg) {
if(!tryAcquire(arg))
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
|
释放操作:
|
1
2
3
4
5
6
7
8
9
|
public final boolean release(intarg) {
if(tryRelease(arg)) {
Node h = head;
if(h !=null && h.waitStatus !=0)
unparkSuccessor(h);
return true;
}
return false;
}
|
The synchronizer framework provides a ConditionObject class for use by synchronizers that maintain exclusivesynchronization and conform to the Lock interface. —— Doug Lea《 The java.util.concurrent Synchronizer Framework 》
以下是AQS队列和Condition队列的出入结点的示意图,可以通过这几张图看出线程结点在两个队列中的出入关系和条件。




0赞JUC (Java Util Concurrency) 基础内容概述的更多相关文章
- JUC (java.util.concurrent)
1.什么是线程?什么是进程? 2.多线程的状态? public enum State { //6种状态 NEW, RUNNABLE, //可运行 BLOCKED, //阻塞 WAITING, //等待 ...
- Java并发编程基础-线程安全问题及JMM(volatile)
什么情况下应该使用多线程 : 线程出现的目的是什么?解决进程中多任务的实时性问题?其实简单来说,也就是解决“阻塞”的问题,阻塞的意思就是程序运行到某个函数或过程后等待某些事件发生而暂时停止 CPU 占 ...
- java.util.concurrent包
在JavaSE5中,JUC(java.util.concurrent)包出现了 在java.util.concurrent包及其子包中,有了很多好玩的新东西: 1.执行器的概念和线程池的实现.Exec ...
- 深入理解java:2.3. 并发编程 java.util.concurrent包
JUC java.util.concurrent包, 这个包是从JDK1.5开始引入的,在此之前,这个包独立存在着,它是由Doug Lea开发的,名字叫backport-util-concurrent ...
- 《The java.util.concurrent Synchronizer Framework》 JUC同步器框架(AQS框架)原文翻译
一.论文简介 闲来无事,看看源码,发现了一篇JDK作者的论文<The java.util.concurrent Synchronizer Framework>主要描述了作者对Abstrac ...
- Java 数组基础,java.util.Arrays
定义数组 方式1(推荐,更能表明数组类型) 方式2(同C语言) 方式3定义时直接初始化 数组运用基础 数组长度 equals() 数组元素不为基本数据类型时 二维数组 二维数组基础 变长的二维数组 j ...
- java基础-学java util类库总结
JAVA基础 Util包介绍 学Java基础的工具类库java.util包.在这个包中,Java提供了一些实用的方法和数据结构.本章介绍Java的实用工具类库java.util包.在这个包中,Java ...
- java util包概述
util是utiliy的缩写,意为多用途的,工具性质的包这个包中主要存放了:集合类(如ArrayList,HashMap等),随机数产生类,属性文件读取类,定时器类等类.这些类极大方便了Java编程, ...
- Java Concurrency - java.util.concurrent API Class Diagram
摘自: www.uml-diagrams.org Here we provide several UML class diagrams for the Java™ 7 java.util.concur ...
- java基础——java.util.ConcurrentModificationException
在编写代码的时候,有时候会遇到List里有符合条件的的对象,就移除改对象! 但是这种操作如:使用了 List 的remove,会导致一些很严重的问题! 如下这段代码使用ArrayList: @Test ...
随机推荐
- three.js实现太阳系
前言 刚开始使用three.js时会不太熟悉,想写一些项目增加理解,网上翻了好多文章,不是画立方体就是画三角形,最后偶然看到这个网站,十分炫酷. 我们也许没那么牛逼,但我们可以整个简略版的太阳系来练练 ...
- 深度学习环境安装-conda-torch-Jupyter Notebook
conda的安装 为什么要安装这个,它是什么? 它是一个管理环境的,当我们跑项目的时候,往往这些项目所需要的pickets库和环境是不同的,这时候如果自己的电脑里面只有一个版本的库的话,就运行不了,比 ...
- 所见即所得,赋能RAG:PDF解析里的段落识别
前几天,有一位用户使用OCR产品识别多栏论文后向我们询问:要怎么解决不合适的断句.分段以及错误阅读顺序的问题? 我们用一个相似案例为大家直观展示这位用户遇到的情况. 如图中的多栏期刊,如果用OCR识别 ...
- JavaScript – Proxy
参考 阮一峰 – Proxy 介绍和使用 Proxy 的作用是代理对象, 消费者不直接使用对象, 而是使用代理对象. 一般上做代理的目的就是想拦截对象访问做一些别的事情. 比如当对象改变以后, 同步 ...
- SQL server temporal table 学习笔记
refer: https://blog.csdn.net/Hehuyi_In/article/details/89670462 https://docs.microsoft.com/en-us/sql ...
- 系统编程-操作系统概论PART2
<1> 操作系统提供的服务 <2> 现代操作系统的特征 2.1 微内核(英文中常译作µ-kernel或者micro kernel) 这是一种能够提供必要服务的操作系统内核: ...
- 数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪
数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪 随着全球对可持续发展和产品透明度的关注日益增加,企业需要一种可靠的方法来跟踪和管理产品生命周期中的关键数据.我们 ...
- Atcoder Beginner Contest 367
A.Shout Everyday \(\text{Diff }43\) 给你 \(24\) 小时制下的 \(A,B,C\) 三个时刻,问 \(A\) 是否在 \([B,C]\) 范围内 考虑到先将 \ ...
- 暑假集训PVZ提高模拟9
没关 exe 让这货挂了一天 UPD:又挂了一晚上,现在被我正义制裁了 A.大众点评 交互红题啊,交互会写,但是忘记判 \(n=1\) 了 这个题交互库函数实现起来还是挺简单的,我 Windows 不 ...
- iOS生成ipa包的时候总是弹窗提示macOS想要使用系统钥匙串
最近新换了一台苹果电脑,性能不错,不过证书和描述文件需要重新配置,遇到了一系列奇怪的问题.在这里整理记录下来,希望能给其他人提供一些帮助.iOS生成ipa包的时候总是弹窗提示[macOS想要使用系统钥 ...