1、concurrent包层次结构:
包内有atomic和lock两个子包,还有阻塞队列以及executors,并发容器,同步工具;这些的实现都依赖于volatile变量读写和CAS
2、lock简介
含义:java5之后增加了lock接口,提供和synchronized一样的锁功能;其操作灵活性更强,锁的种类更丰富支持非公平锁和可重入锁且可以自定义去实现不同功能需求的锁-公平,非公平,独占,共享; 可获取锁的状态-解决死锁问题;
API
void lock(); //获取锁
void unlock(); //释放锁
void lockInterruptibly() throws InterruptedException;//获取锁的过程能够响应中断
boolean tryLock();//非阻塞式响应中断能立即返回,获取锁放回true反之返回fasle
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//超时获取锁,在超时内或者未中断的情况下能够获取锁
Condition newCondition();//获取与lock绑定的等待通知组件,当前线程必须获得了锁才能进行等待,进行等待时会先释放锁,当再次获取锁时才能从等待中返回
Lock的除Condition以外的demo:
package com.hezm.thread.day1.theory;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
//可重入锁(当前线程调用lock.lock()方法可以再次lock.lock进入,不会阻塞)
// 解决:死锁问题;
//synchronized也是支持可重入的;
static Lock lock = new ReentrantLock();
static Lock lock1 = new ReentrantLock(true); //公平锁
//可重入锁特性;
public static void main(String[] args) {
//场景1:获取锁,成功则进入,不成功则阻塞等待一个一个执行
lock.lock(); //获得锁
demo1()
lock.unlock(); //释放锁
}
/**synchronized 重入互斥锁 案例*/
public synchronized void demo1() {
System.out.println("我锁住了this,我想再次进入获取锁");
demo2();
}
public synchronized void demo2() {
synchronized (this){
System.out.println("我再次进来了,增加重入次数1");
//todo
}
System.out.println("我出来第一把锁了,减少重入次数1");
}
//场景2:如果已经被lock则立即返回false,忽略操作,不会等待。
public synchronized void demo3() {
if(lock.tryLock()){
try{
}finally{
lock.unlock
}
}
}
//场景3:如果发现该操作已经在执行,则等待一段时间,超时则不执行
public synchronized void demo3() {
if(lock.tryLock(5,TimeUnit.SECONDS)){
try{
}finally{
lock.unlock
}
}
}
//场景4:如果发现该操作已经在执行,等待执行,可中断正在执行的操作立刻释放锁,继续下一操作
public synchronized void demo3() {
try{
lock.lockInterruptibly();
}finally{
lock.unlock
}
}
}
Contion我们有专门的分析文章,详情请看线程控制工具案例详解;
要想弄清楚lock的底层原理,和其他类相比我们还不能马上进入看源码的过程,我们还需要先了解其构建的基础:AQS(AbstractQueuedSynchronizer);
3、AQS详解:
含义:
抽象队列同步器,是其他同步组件的基础框架,依赖一个int成员变量来表示同步状态以及通过FIFO队列构成等待队列;
AQS采用模板方法的设计模式,在同步组件的实现中推荐定义继承AQS的静态内存类;
子类重写AQS的tryAcquire-独占式获取同步状态、tryRelease-独占式释放同步状态、tryAcquireShared-共享式获取同步状态、tryReleaseShared-共享式释放同步状态、isHeldExclusively-是否被当前线程独占; 方法实现对锁的自定义实现;
状态的更新使用getState,setState以及compareAndSetState这三个方法;
AQS封装了同步状态的管理,线程排队,等待和唤醒等操作;
核心API:
AQS提供的模板方法: 分为:
独占式获取和释放同步状态 : acquire() , acquireInterruptibly(), tryAcquireNanos(), release()
共享式获取和释放同步状态: acquireShared(), acquireSharedInterruptibly(), tryAcquireSharedNanos(), releaseShared();
查询同步队列中等待线程情况:getQueuedThreads()
数据结构:
head指向头结点,tail指向尾节点的单向链表结构;head始终保持线程值为null,正在被执行的线程保存在AbstractOwnableSynchronizer ;
* +------+ prev +-----+ +-----+
* head | node | <----| node | <---- | node | tail
* +------+ +-----+ +-----+
waitStatus常量值含义:
CANCELLED 1 同步队列中等待的线程超时或被中断,需要从同步队列中取消该Node的节点,节点终极状态
SIGNAL -1 等待唤醒状态的后继结点,当其前继结点的线程释放了同步锁或被取消,将会通知后继节点的线程执行;
CONDITION -2 标识节点处于等待队列中,当其他线程调用Condition.signal()该状态的节点从等待队列转移到同步队列中,等待获取同步锁;
PROPAGATE -3 共享模式中标识结点的线程处于可运行状态
0 0 初始状态
AQS核心模板API源码分析:
独占锁获取方法acquire():
//独占锁获取
public final void acquire(int arg) {
//获取同步状态成功,则方法结束返回
//若失败则先调用addWaiter()方法再调用acquireQueued()方法实现入队列操作;
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private Node addWaiter(Node mode) {
// 1. 将当前线程构建成Node类型
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 2. 当前尾节点是否为null?
Node pred = tail;
if (pred != null) {
// 2.2 将当前节点尾插入的方式插入同步队列中
node.prev = pred;
if (compareAndSetTail(pred, node)) { //CAS操作自旋重试
pred.next = node;
return node;
}
}
// 2.1. 当前同步队列尾节点为null,说明当前线程是第一个加入同步队列进行等待的线程
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) { //自旋插入队列
Node t = tail;
if (t == null) { // 第一个进入的时候首先构造头结点;
//1. 构造头结点
if (compareAndSetHead(new Node())) //cas操作设置头节点
tail = head;
} else {
// 2. 尾插入,CAS操作失败自旋尝试
node.prev = t;
if (compareAndSetTail(t, node)) { //cas操作设置尾结点
t.next = node;
return t;
}
}
}
}
//获取锁成功/失败的处理
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { //自旋
// 1. 获得当前节点的先驱节点
final Node p = node.predecessor();
// 2. 当前节点能否获取独占式锁
// 2.1 如果当前节点的先驱节点是头结点并且成功获取同步状态,即可以获得独占式锁
if (p == head && tryAcquire(arg)) { //获取锁成功的出队操作;
//队列头节点指向当前节点,当前节点的prev设置为null,
setHead(node);
//帮助GC前驱节点
p.next = null; // help GC
failed = false;
return interrupted;
}
// 2.2 获取锁失败,线程进入等待状态等待获取独占式锁
if (shouldParkAfterFailedAcquire(p, node) && //cas将节点状态设置成SIGNAL,表示线程阻塞;失败则返回false,自旋调用acquireQueued()重试;
parkAndCheckInterrupt()) //阻塞线程
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
独占锁的释放 release()唤醒后继节点:
public final boolean release(int arg) {
if (tryRelease(arg)) { //释放成功;
Node h = head;
if (h != null && h.waitStatus != 0) //节点存在且状态值不为0执行unpark()唤醒
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//头节点的后继节点
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//为何从尾节点遍历?
// {enq()中1,prev赋值 2,tail指向 3,next赋值}时候不是原子操作,从头节点遍历会造成链表断裂;
for (Node t = tail; t != null && t != node; t = t.prev) //
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//后继节点不为null时唤醒该线程
LockSupport.unpark(s.thread);
}
可中断式获取锁acquireInterruptibly():
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
//线程获取锁失败
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
//将节点插入到同步队列中
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
//获取锁出队
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//线程中断抛异常
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
超时等待式获取锁 tryAcquireNanos()
共享锁的工作逻辑:
1、当线程调用acquireShared()申请获取锁资源,成功则进入临界区
2、当获取锁失败时,则创建一个共享类型的节点并进入一个FIFO等待队列,被挂起等待唤醒
3、当队列中等待线程被唤醒后重新尝试获取锁资源,成功则唤醒后面还在等待的共享节点并把该唤醒事件传递下去,即依次唤醒在该节点后面的所有共享节点,然后进入临界区,否则继续挂起等待;setHeadAndPropagate() 方法实现;
共享锁获取acquireShared() :
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) // 大于等于0表示获取锁成功
doAcquireShared(arg); //锁获取失败加入队列
}
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
// 当该节点的前驱节点是头结点且成功获取同步状态
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return; //自旋退出条件:当前节点的前驱节点是头节点且获取锁成功
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // 记录下旧头节点以便检查
setHead(node); //把当前获取的锁节点设置为头节点
//执行唤醒操作的两种情况:①调用方指明后继节点需要被唤醒;
// ②头节点的后面节点需要被唤醒
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared()) //如果当前节点的后继节点是共享类型,则唤醒后继节点;
doReleaseShared();//唤醒后继节点
}
}
//唤醒后继节点
private void doReleaseShared() {
for (;;) { //自旋
Node h = head; //唤醒由新的头节点开始
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) { //表示该后继节点需要被唤醒;
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) //控制并发setHeadAndPropagate 和 release()两个入口;
continue; // loop to recheck cases
unparkSuccessor(h); //执行唤醒操作
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // 如果后继节点不需要唤醒则把当前节点状态设置为PROPAGATE,确保以后可以传递下去;
}
if (h == head) // 如果头节点发生变化,说明其他线程获取到了锁,进行重试,以使得唤醒动作可以传递;
break;
}
}
共享锁释放releaseShared():
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) { //成功释放同步状态后
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases //再检查重置状态是否失败
unparkSuccessor(h); // 唤醒成功,之前的头结点阻塞进入setHeadAndPropagate(node, r)处理逻辑;
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) //没有后继者设置状态
continue; // loop on failed CAS
}
if (h == head) // 如果头节点发生变化进行重试
break;
}
}
其他关键点
synchronized 与lock的区别?
* ① synchronized是一个关键词,lock是一个接口 {基本实现:tryLock, unLock};
* ② synchronized:是被动释放锁,同步代码块执行完或异常的时候释放。
* lock可以主动选择什么时候获得锁,释放锁;
* ④ lock有公平锁和非公平锁 ; synchronized只有非公平锁
* ④ lock可以判断锁的状态,可解决死锁问题tryLock(), synchronized无法判断。
独占锁和共享锁的区别?
独占(互斥-不可共用)
共享(共享-可以共享);
可重入锁的源码分析:
//可重入锁特性;
public static void main(String[] args) {
//场景1:获取锁,成功则进入,不成功则阻塞等待一个一个执行
lock.lock(); //获得锁
demo1()
lock.unlock(); //释放锁
}
//以下分析走非公平锁逻辑:
//lock()
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//独占式获取锁,重写了tryAcquire()逻辑;
// 当线程状态 == 0 则抢锁,抢成功则执行(插队);
// 不为0为当前正在执行线程重入的则状态值计数 +1;(可重入锁的逻辑)
// 以上都不是则返回false继续执行
acquire(1);
}
//unlock()
public final boolean release(int arg) {
if (tryRelease(arg)) { //重写了释放逻辑
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) { //可重入锁
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { //归置为0,释放锁成功
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
/***
* 学习笔记:
*
* Lock的实现原理(如何实现多个线程在竞争的时候的阻塞和同步):
* 实现有:读写锁、 重入锁 ReentrantLock (唯一实现){
* 核心实现: 抽象队列同步器 AbstractQueuedSynchronizer --> 实现 Sync 包含 FairSync 与 NofairSync 两种具体实现{
* 核心功能:独占(互斥-不可共用)、共享(共享-可以共享);
*
* lock.lock()核心调用方法:
* NofairSync # compareAndSetState(0, 1)
* 1,当保存的值为0的时候则锁抢占成功,争抢锁成功,设置内存偏移量为1,则执行当前线程(setExclusiveOwnerThread(Thread.currentThread()););
* Unsafe # compareAndSwapInt(Object 锁对象, long 内存地址偏移量, int 预期值, int 修改值):
* 【解释】:当预期值和偏移量中值相等则修改值返回true,否则不修改返回false;
* 2,当锁已经被抢占,后面进来的线程则调用acquire(1);
* !tryAcquire(arg){
* NofairSync # nonfairTryAcquire(acquires) 返回true/false{
* 当线程状态 == 0 则抢锁,抢成功则执行(插队);
* 不为0为当前正在执行线程重入的则状态值计数 +1;
* 以上都不是则返回false继续执行 acquireQueued(addWaiter(Node.EXCLUSIVE), arg);
* }
* }
*
* AbstractQueuedSynchronizer # addWaiter(Node.EXCLUSIVE), arg{
* 把当前线程封装为独占锁(Node.EXCLUSIVE)Node{
* 1、tail不为null(队列不存在其他线程){
* 传入的node{thread = threadC;waitState = 0 }节点pre设置为node{thread = threadB;waitState = 0 };
* 把AQS实现将tail指向 node{thread = threadC;waitState = 0 }
* node{thread = threadB;waitState = 0 }中的next指向node{thread = threadC;waitState = 0 }
* }
* 2、tail为null则调用 enq(node) 自旋锁实现(首次进来自旋两次){
* 1,Cas初始化一个空的Node节点( thread = null;waitState = 0 ),head 和 tail节点都指向他;
* 2, 传入的node{thread = threadB;waitState = 0 }节点pre设置为 前面初始话的Node节点;
* 把AQS实现将tail指向 node{thread = threadB;waitState = 0 }
* 初始化的空Node中的next指向node{thread = threadB;waitState = 0 }
* }
* }
* }
* AbstractQueuedSynchronizer # acquireQueued(node){
* final Node p = node.predecessor(); 拿到线程B的前一个节点:头节点
* tryAcquire(arg) 争抢锁
* shouldParkAfterFailedAcquire(p, node) 争抢锁失败后是否 park{
* 1、查看p线程的状态是否为 SINGLA 该状态的线程在锁释放的时候会被唤醒; 是则park
* 2、为 CANAELLED 的时候,线程超时或被中断的时候 把 CANAELLED 的节点丢掉;
* 3、标注当前节点的前一个节点为 SINGLA {初始化的Node, node{thread = threadB;waitState = 0 } 均被改变}
* }
* parkAndCheckInterrupt(){
* LockSupport.park(this); 挂起当前的线程;( node{thread = threadB;waitState = 0 } node{thread = threadC;waitState = 0 } )被挂起
* Thread.interrupted(); 复位,返回是否被中断过的标志;【线程在park中无法响应线程的中断信息】
* }
* }
* selfInterrupt();该方法响应之前将中断线程设置为true的设定;中断当前线程: Thread.currentThread().interrupt();
*
*
* lock.unlock()核心调用方法:将状态值修改、唤醒下一个线程{
* tryRelease() 设置状态值为0,修改当前执行线程为null;
* unparkSuccessor(node){
* 更新初始化的Node 的 waitState 为1;
* 下一个节点不为null,则LockSupport.unpark( s.thread ); //唤醒线程B
* 下个节点为null,(当节点状态为Cancelled)的时候从尾部进行遍历,移除Cancelled的节点;{
* 这个设计原因:尾部遍历避免尾部的链构建原子性问题{enq()中1,prev赋值 2,tail指向 3,next赋值}造成断链;
* 原因:第3步next未赋值时中断断链,而prev第一步肯定已经完成;
* }
* }
* }
* 唤醒之后线程在 AbstractQueuedSynchronizer # acquireQueued(node)中的线程被唤醒继续执行;走之后的逻辑;{
* 1,head设置为threadB,把原来Node(threadB)的线程设置为null,pre设置为null;
* 2,将之前Head的next设置为null{删除之前的head节点}; Node(threadB)变成了头节点;
* }
* }
* }
* 【补充】:
* 【中断通信】
* Thread.interrupted(); //获取中断状态并复位
* thread.interrupt(); //中断线程
* 【变量通信新机制】:
* LockSupport.unpark( s.thread ) 唤醒线程
* LockSupport.park( s.thread ) 挂起阻塞线程
* 【公平锁和非公平锁的区别】
* 1,锁基本原理: 一次只能有一个线程获取到锁,而能获取到锁的标志是AQS.STATE = 0 大于0表示已经有线程获取到锁了;获取到锁则状态值+1;
* 2,区别
* lock() tryAcquire()
* fair !hasQueuePredecessors如果队列有
* 阻塞则不进行抢占
* noFair : lock直接插队抢占;
*
*/
公平锁 VS 非公平锁:
公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。
公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。因此,ReentrantLock默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量。
demo2:ReentrantReadWriteLock
package com.hezm.thread.day1.theory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**可重入读写锁*/
public class ReentrantReadWriteLockDemo {
static ReentrantReadWriteLock writeLock = new ReentrantReadWriteLock(); //可重入读写锁;读不需要加锁。
static Map<String, Object> cacheMap = new HashMap<>();
static Lock read = writeLock.readLock();
static Lock write = writeLock.writeLock();
public static void main(String[] args) {
/**
* 适用于:读多写少的场景:
* 读->读可以共享 // Thread2:在get的时候, Thread1:线程在get的时候不会阻塞,可共享数据;
* 读->写互斥 // Thread2:在put的时候, Thread1:线程在get的时候会阻塞,等待写完才读,读到最新数据;
* 写->写互斥 // Thread2:在put的时候, Thread1:线程在put的时候会阻塞,等待写完才put;
*/
put("zhangsan","23岁");
get("zhangsan");
}
//Thread1:读锁:读的时候获取最新数据
public static final Object get(String key){
System.out.println("begin read deta:" + key);
read.lock();
try {
return cacheMap.get(key);
} finally {
read.unlock(); //防止死锁,一直不释放锁
}
}
//Thread2:写锁:写操作对读可见
public static final Object put(String key, Object value){
write.lock();
try {
System.out.println("begin write deta:" + key + "value" + value);
return cacheMap.put(key,value);
} finally {
write.unlock(); //防止死锁,一直不释放锁
}
}
}
使用场景:ReentrantLock是独占锁, 在大部分为读数据,很少写数据的场景下,这就是性能提升的关键点;而ReentrantReadWriteLock就为这种场景提供了解决机制,在都是读线程访问的时候,可以多线程同时运行,在写线程访问的时候,所有的读线程和写线程会被阻塞;
核心机制:
①读写锁如何分别记录读写状态
②写锁如何获取和释放锁
③读锁怎样获取和释放的?
④锁降级如何实现:写锁 降级 为读锁;
源码分析:
①读写锁如何分别记录读写状态
// 同步状态的低16位表示写锁获取次数; c & EXCLUSIVE_MASK;
// 同步状态的高16位表示读锁被获取的次数 c >>> SHARED_SHIFT;
②写锁如何获取和释放锁
//写锁是独占锁,通过重写tryAcquire() 方法实现写锁的抢占;
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
// 1. 获取写锁当前的同步状态
int c = getState();
// 2. 获取写锁获取的次数
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
// 3.1 当读锁已被读线程获取或者当前线程不是已经获取写锁的线程的话
// 当前线程获取写锁失败
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 同步状态的低16位表示写锁获取次数; c & EXCLUSIVE_MASK;
// 同步状态的高16位表示读锁被获取的次数 c >>> SHARED_SHIFT;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
// 3.2 当前线程获取写锁,支持可重复加锁
setState(c + acquires);
return true;
}
// 3.3 写锁未被任何线程获取,当前线程可获取写锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
//写锁的释放,重写AQS的tryRelease方法实现
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//1. 同步状态减去写状态
int nextc = getState() - releases;
//2. 当前写状态是否为0,为0则释放写锁
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
//3. 不为0则更新同步状态
setState(nextc);
return free;
}
③读锁怎样获取和释放的?
//读锁的获取
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
//1. 如果写锁已经被获取并且获取写锁的线程不是当前线程的话,当前
// 线程获取读锁失败返回-1
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
//2. 当前线程获取读锁 SHARED_UNIT,高16位+1
compareAndSetState(c, c + SHARED_UNIT)) {
//3. 下面的代码主要是新增的一些功能,比如getReadHoldCount()方法
//返回当前获取读锁的次数
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
//4. 处理在第二步中CAS操作失败的自旋已经实现重入性
return fullTryAcquireShared(current);
}
//读锁释放
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
// 前面还是为了实现getReadHoldCount等新功能
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
// 读锁释放 将同步状态减去读状态即可
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;
}
}
④锁降级如何实现:写锁 降级 为读锁;
遵循规则:按照获取写锁,获取读锁 再释放写锁的次序,写锁能够降级成为读锁; 不支持锁升级
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// 获取写锁之前必须释放读锁
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// 降级通过在释放写锁之前获取读锁
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // 解锁写,仍然保持读
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}