写锁之基于AQS加锁
首先,这个lock的流程都是一样的。
唯有不同的就是tryAcquire的实现是不同的。
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
- int c = getState();获取state,目前是0
- int w = exclusiveCount(c);,state二进制里面的高低16位分别代表了读锁和写锁,高十六位代表读锁,第十六位代表写锁。该w就获取了state的低16位,代表了写锁的加锁次数
- if (c != 0) ;由于此时state=0,这个时候这个判断是false不进入这个代码块中
- writerShouldBlock() ||!compareAndSetState(c, c + acquires),非公平锁现在一定尝试去加锁(因为是返回false)。如果是公平锁就会判断下队列有没有等待线程,有等待线程第一个方法就返回true随后直接return false了,如果没有等待线程是会返回false的,因此就要去执行cas操作,只有当cas设置不成功了才会直接return false。
- writerShouldBlock():非公平锁,此时返回fasle,公平锁判断如果队列里面有等待的线程那么就返回true
- writerShouldBlock():非公平锁,此时返回fasle,公平锁判断如果队列里面有等待的线程那么就返回true
setExclusiveOwnerThread(current);当第4不cas成功了,那么就可以设置thread标记位了。
写锁之可重入加锁
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
//1
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//2
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
//3
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
此时由于是可重入加锁,就会进入if (c != 0) 的判断
w == 0 || current != getExclusiveOwnerThread();因为c!=0,意味着有可能加了写锁或者读锁。
- 如果w是0,有人加了读锁,此时不能加写锁就要返回false。
- 如果w不是0,证明有人加了写锁,随后要判断一下当前线程是不是之前加锁的线程,如果不是就返回false,是的话继续往下走开始重入锁操作。
- if (w + exclusiveCount(acquires) > MAX_COUNT) ,判断加锁是否大于重入最大加锁数,大于的话就要抛异常了
- setState(c + acquires);, return true; 加锁成功设置state值并返回true
写锁加锁失败如何入队阻塞
现在有一个线程2来加锁,由于线程1加锁失败因此线程2要入队列。
在代码层面上那个tryAcquire返回false,然后走acquireQueued(addWaiter(Node.EXCLUSIVE), arg),入队操作都是一样的。