concurrent-锁
AQS
ReentrantLock
ReentrantReadWriteLock
AQS
全名叫AbstractQueuedSynchronizer。
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
//链表队列头和尾
private transient volatile java.util.concurrent.locks.AbstractQueuedSynchronizer.Node head;
private transient volatile java.util.concurrent.locks.AbstractQueuedSynchronizer.Node tail;
//核心属性,竞争的资源指的就是抢占这个属性
private volatile int state;
//当前持有state的线程
private transient Thread exclusiveOwnerThread;
static final class Node {//每个node代表一个线程
//标记线程是因为获取共享资源失败而进入队列的
static final java.util.concurrent.locks.AbstractQueuedSynchronizer.Node SHARED = new java.util.concurrent.locks.AbstractQueuedSynchronizer.Node();
//标记线程是因为获取独占资源失败而进入队列的
static final java.util.concurrent.locks.AbstractQueuedSynchronizer.Node EXCLUSIVE = null;
//表示线程因为中断或者等待超时,需要从等待队列中取消等待
//该状态node会自动被jvm回收
static final int CANCELLED = 1;
//已占有锁的线程释放时会通知该状态节点去抢占资源
static final int SIGNAL = -1;
//标识该节点在condition队列中。
// 备注:lock的每个condition都有一个阻塞队列,当调用了signal方法后,队列中的节点会移动到clh队列中
//类似sychronize的waitSet
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
//队列是双向链表
volatile java.util.concurrent.locks.AbstractQueuedSynchronizer.Node prev;
volatile java.util.concurrent.locks.AbstractQueuedSynchronizer.Node next;
volatile Thread thread;
java.util.concurrent.locks.AbstractQueuedSynchronizer.Node nextWaiter;
}
//用来存放condition阻塞队列
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/**
* First node of condition queue.
*/
private transient AbstractQueuedSynchronizer.Node firstWaiter;
/**
* Last node of condition queue.
*/
private transient AbstractQueuedSynchronizer.Node lastWaiter;
}
}
ReentrantLock
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();//构造器
lock.lock();//加锁
lock.unlock();//解锁
}
构造器:
public class ReentrantLock implements Lock, java.io.Serializable {
//两个实现类:NonfairSync-非公平锁,FairSync-公平锁
//NonfairSync和FairSync都是ReentrantLock内部类
private final java.util.concurrent.locks.ReentrantLock.Sync sync;
public ReentrantLock(boolean fair) {
sync = fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync();
}
public ReentrantLock() {//默认是非公平锁
sync = new ReentrantLock.NonfairSync();
}
}
我们先分析非公平锁。
这里先总结一下lock方法的全部流程:
1、首先通过cas尝试获取state,如果成功,直接更新state持有线程为当前线程
2、如果失败,先判断一下state是否为0,如果为0,再次尝试通过cas获取state,成功更新state持有线程
3、如果失败,则判断一下state当前持有线程是否为当前线程,如果是,则可以重入,直接更新重入次数+1,结束流程
4、如果失败,也不可以重入,那么将当前线程封装到node中,然后通过尾插法插入到队列
插入队列逻辑:
1、首先判断当前队列尾节点是否为null,如果不为null,则通过cas插入尾部
2、如果当前队列尾节点为null,则初始化一个队列,然后通过cas插入尾部。
如果当前队列尾节点不为null,但是在第一步cas插入失败,则通过cas方式循环不停向尾部插入。
总之这一步完成后节点一定是插入到队列尾部了。
接下来的逻辑是在一个无限循环内部:
1、首先判断一下当前节点的前驱节点是否为队列头部,如果是头部,则该节点再次尝试获取state,如果成功,则将当前节点更新为队列头部。
2、如果不是头部,或者获取state失败,则当前节点通过LockSupport.park(this);方法阻塞自己。
注意:这是在一个循环里阻塞的,当被唤醒时,循环会继续。
加锁:
ReentrantLock.NonfairSync.lock():首先cas方式尝试更新state,成功更新state持有者为当前线程,失败进入acquire方法。
// ReentrantLock.NonfairSync
final void lock() {
if (compareAndSetState(0, 1))//cas方式更新state值为1
setExclusiveOwnerThread(Thread.currentThread());//入口
else
acquire(1);//入口
}
// AbstractOwnableSynchronizer
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
AbstractOwnableSynchronizer.acquire:
// AbstractOwnableSynchronizer
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg))
selfInterrupt();
}
接下来分别看上面的四个方法:tryAcquire、acquireQueued、addWaiter、selfInterrupt。
tryAcquire:再次尝试直接获取state,如果失败在判断是否是锁重入
// ReentrantLock.NonfairSync
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// ReentrantLock.NonfairSync
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//如果state没有线程占用
if (compareAndSetState(0, acquires)) {//尝试获state
setExclusiveOwnerThread(current);//获取state成功,更新锁持有线程为当前线程
return true;
}
}
//如果state已经有线程占用了,但是占用的线程为当前线程,就是锁重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;//更新锁重入次数 +1
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;//其他情况返回false
}
addWaiter:创建一个node,添加到aqs队列尾部,如果失败(多线程竞争)通过循环cas方式知道成功为止。
// AbstractOwnableSynchronizer
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;//尾插法
if (pred != null) {//如果队列原尾节点不为null,将当前节点插入到队列尾部
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//代码如果走到这里有两种情况,1:队列还未初始化即队列原尾节点为null。
// 2:刚才向队列尾部插入节点失败,有其他线程竞争插入。即compareAndSetTail(pred, node)失败
enq(node);//入口
return node;
}
private AbstractQueuedSynchronizer.Node enq(final AbstractQueuedSynchronizer.Node node) {
//该方法是往队列尾部插入节点
// 当队列还未初始化时,会初始化一个队列,然后插入到尾部
//当队列已经初始化,则通过cas方式,失败不停重试,向尾部插入节点
for (;;) {//cas方式 失败不停循环
AbstractQueuedSynchronizer.Node t = tail;
if (t == null) { // 如果队列原尾节点为null,
if (compareAndSetHead(new AbstractQueuedSynchronizer.Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
AbstractOwnableSynchronizer.acquireQueued:参数是刚才添加到队列中的node,该方法判断node的前驱节点是否是链表head,如果是,尝试获取state,如果成功,则更新node为链表head。
如果失败,则阻塞住自己,这是在一个循环中阻塞。等到state持有线程释放锁会唤醒阻塞线程,循环继续。
// AbstractOwnableSynchronizer
final boolean acquireQueued(final AbstractQueuedSynchronizer.Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final AbstractQueuedSynchronizer.Node p = node.predecessor();//获取前驱节点
if (p == head && tryAcquire(arg)) {//如果前驱节点是头节点,则尝试获取state
//如果获取state成功了,则将当前node(state持有线程的node)设置为链表的head
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
//如果不是头节点或者获取state失败,则阻塞自己。
//注意:这是在一个循环里阻塞的,当被唤醒时,循环会继续
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
锁释放
ReentrantLock.unlock:
1、首先将state值减去1,如果state大于0,证明锁重入,直接结束。如果state等于0,还要更新state当前持有线程为null
2、从头节点开始往下找,如果节点状态不为-1,则更新其状态为作废(jvm自动回收),直到找到第一个状态为-1的节点,然后调用唤醒。
这样就和上面在循环中阻塞连接起来了。
// ReentrantLock
public void unlock() {
sync.release(1);
}
//AbstractOwnableSynchronizer
public final boolean release(int arg) {
if (tryRelease(arg)) {
AbstractQueuedSynchronizer.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);//更新state持有线程未null
}
setState(c);//如果锁的重入次数未降到0,则直接更新state次数-1就结束
return free;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)//如果节点的状态小于0,则通过cas更新为0
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;//获取节点的next节点
if (s == null || s.waitStatus > 0) {//如果next节点为null或者状态不为-1,则将next节点更新为null
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)//将最近的状态为-1的节点设置给s
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//唤醒
}
上面是非公平锁的分析,公平锁又是什么样的?
非公平锁是先获取锁,失败进入队列。
公平锁先进入队列,排队获取锁。
ReentrantReadWriteLock
public static void main(String[] args) {
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();
//读和读不互斥
new Thread(new Runnable() {
@Override
public void run() {
try {
readLock.lock();
Thread.sleep(3000);
readLock.unlock();
System.out.println("读锁释放");
}catch (Exception e){
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(1000);
readLock.lock();
System.out.println("获取到读锁");
readLock.unlock();
}catch (Exception e){
}
}
}).start();
}
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
private final java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock readerLock;
private final java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock writerLock;
final java.util.concurrent.locks.ReentrantReadWriteLock.Sync sync;
}
//重入计数器,读锁 -- 可重入锁 , 该属性用来记录每个线程重入读锁次数
static final class HoldCounter {
int count = 0;
final long tid = getThreadId(Thread.currentThread());
}
//HoldCounter是用来记录一个线程的次数 readHolds存储所有线程的HoldCounter
private transient ThreadLocalHoldCounter readHolds;
static final class ThreadLocalHoldCounter//一个内部类的定义
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
/** 最近一个成功获取读锁的线程的计数。这省却了ThreadLocal查找 缓存*/
private transient HoldCounter cachedHoldCounter;
state是32位int类型的,ReentrantReadWriteLock将其拆分成两段,高16位用来表示读锁数量,低16位用来表示写锁数量。
分别分析四个方法:readLock.lock readLock.unlock writeLock.lock writeLock.unlock
readLock.lock
先总结流程:
1、首先获取state写锁段位,如果大于0,证明有写锁,进入插入队列逻辑(同ReentrantLock一样)
2、如果state写锁段位小于0,则通过循环cas方式更新读锁直到成功。
有几点需要注意:
1、第一个读锁单独缓存到firstReader,性能提升
2、通过ThreadLocal和HoldCounter类来存储每个线程锁state的值
// ReentrantReadWriteLock
public void lock() {
sync.acquireShared(1);
}
//AbstractQueuedSynchronizer
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// ReentrantReadWriteLock
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
//state写锁位段大于0,并且当前线程不是state持有线程,直接返回-1
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);//获取state写锁位段
if (!readerShouldBlock() &&
r < MAX_COUNT &&//
compareAndSetState(c, c + SHARED_UNIT)) {
//如果读锁数量未达到上线,并且cas获取state读锁成功
if (r == 0) {
//state的第一个读锁单独用firstReader来存储,增加性能
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
//当前线程就是firstReader
firstReaderHoldCount++;
} else {//通过ThreadLocal类型的HoldCounter来存储锁的次数
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;
}//当上面代码获取锁失败后,这个方法会通过cas循环获取锁,直到成功。 逻辑同上
return fullTryAcquireShared(current);
}
// ReentrantReadWriteLock
final int fullTryAcquireShared(Thread current) {
ReentrantReadWriteLock.Sync.HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
AbstractQueuedSynchronizer.doAcquireShared:
//AbstractQueuedSynchronizer
private void doAcquireShared(int arg) {
//首先添加节点到队列尾部,逻辑同ReentrantLock
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {//如果前驱节点是头节点,则尝试获取state
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
//如果不是头节点或者获取state失败,则阻塞自己。
//注意:这是在一个循环里阻塞的,当被唤醒时,循环会继续
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
释放读锁
//ReentrantReadWriteLock
public void unlock() {
sync.releaseShared(1);
}
// AbstractQueuedSynchronizer
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
分别调用了两个方法:tryReleaseShared和doReleaseShared
ReentrantReadWriteLock.tryReleaseShared:更新holdCounter和state
// ReentrantReadWriteLock
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
//如果当前线程是firstReader,firstReaderHoldCount减一,更新firstReader
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {//更新ThreadLocalHoldCounter
ReentrantReadWriteLock.Sync.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 (;;) {//循环cas更新state
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
AbstractQueuedSynchronizer.doReleaseShared:队列节点处理
// AbstractQueuedSynchronizer
private void doReleaseShared() {
for (;;) {
AbstractQueuedSynchronizer.Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == AbstractQueuedSynchronizer.Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, AbstractQueuedSynchronizer.Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, AbstractQueuedSynchronizer.Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
释放比较简单:更新HoldCounter,更新队列node。
写锁的上锁和释放锁和ReentrantLock是一样的。