概述
通过这节的学习,可以明白显示锁的实现,以及可以创建自己的锁。
我们常见的显示锁,以及其他的锁工具有ReentrantLock
,CountDownLatch
,Semaphore
。它们有一些共同的特点就是状态控制,而对于状态的控制,它们都使用了AQS(AbstractQueuedSynchronizer)
实现。
AQS
如果一个类想实现状态依赖,可以使用AQS
实现。AQS
负责管理同步器类中状态,它管理着一个整数状态信息,可以通过getState
,setState
以及compareAndSetState
等protected
方法来进行操作。这个整数可以表示任意状态,比如CountDownLatch
表示计数,Semaphore
表示剩余的许可数量等等。
如果某个同步器支持支持独占的获取操作,那么需要实现一些保护方法,包括
tryAcquire
,tryRelease
,isHeldExclusively
。而支持共享的同步器,则实现tryAcquireShared
,tryReleaseShared
等方法。AQS
中的acquire
,acquireShared
,release
和releaseShared
等方法使用模板方法模式来组织,都将会分别调用子类中的tryAcquire
,tryAcquireShared
,tryRelease
,tryReleaseShared
。部分摘录自《java并发编程实战》
后面有时间再进一步分析AQS
的实现。
CountDownLatch
CountDownLatch
作为计数的闭锁,也是通过AQS
实现。基本结构如下:
CountDownLatch
依赖Sync
,而Sync
继承自AQS
。后面我们来一起分析一下源码。
Sync的实现
首先我们先来分析CountDownLatch
的内部类Sync
,我们将一个个函数分析。
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
}
Sync
继承自AQS
,构造函数中是通过setState
设置状态即最大的许可数量。
private static final class Sync extends AbstractQueuedSynchronizer {
......
int getCount() {
return getState();
}
}
getCount()
返回当前剩余的许可数量。
private static final class Sync extends AbstractQueuedSynchronizer {
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
}
看到tryAcquireShared
蒙圈了,What?SXXX!前面我们说过AQS
的acquireShared
方法将调用子类tryAcquireShared
,那就需要看看AQS
的acquireShared
的方法了。
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
原来如此!acquireShared
方法中,原来tryAcquireShared(arg) < 0
,才会继续执行,所以Sync
的tryAcquireShared
才会那样写,状态等于0,直接返回1,否则返回-1。
继续分析Sync
的tryReleaseShared
private static final class Sync extends AbstractQueuedSynchronizer {
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
先是获取当前的状态值(剩余的计数),等于0没有了剩余的计数直接返回false。不满足接着使用compareAndSetState
更新状态,我的更新就更新了,为啥子还有个for死循环呢?我们看看compareAndSetState
怎么实现的。
protected final boolean compareAndSetState(int expect, int update) {
return U.compareAndSwapInt(this, STATE, expect, update);
}
原来是使用CAS,CAS是比较并交换,示意图有点类似下图,是现代处理器对并发的支持。详细内容请Google。
说完CAS计数,回到我们的tryReleaseShared
方法,添加死循环是重试逻辑,无法更新,继续重试。到此我们对Sync
已经分析完成。
CountDownLatch的实现
CountDownLatch
的实现全仰仗Sync
。我们分析的时候只分析关键的接口
await方法
public class CountDownLatch {
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
......
}
await
方法调用了直接调用了acquireSharedInterruptibly
方法。ASQ已经实现此方法。
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
首先检测线程的状态,然后通过tryAcquireShared(arg)
是否进入等待队列( doAcquireSharedInterruptibly(arg)进入等待,我们不做进一步的分析)。我们Sync
实现的是
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
因此当没有剩余的许可数,也就是CountDownLatch
接入了结束态,直接await
就不等待了。而当还有剩余的许可数时,就会进入等待。
countDown方法
public class CountDownLatch {
......
public void countDown() {
sync.releaseShared(1);
}
......
}
直接调用了releaseShared
方法了。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
//唤醒线程
doReleaseShared();
return true;
}
return false;
}
Sync实现了tryReleaseShared
。
private static final class Sync extends AbstractQueuedSynchronizer {
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
只用当nextc
等于0的时候才会返回true,也就是没有了许可数了,此时就会唤醒线程。
总结
从上面的分析,我们可以得出,在await
中会Sync
根据tryAcquireShared
进行判断要不要进入等待状态,而countDown
中会调用Sync
的tryReleaseShared
的来递减许可书,直到许可数为0,通知等待的线程。
ReentrantLock
ReentrantLock
的基本结构。
NoFairSync
对于ReentrantLock
,以NoFairSync
来分析
如果某个同步器支持支持独占的获取操作,那么需要实现一些保护方法,包括
tryAcquire
,tryRelease
,isHeldExclusively
。
lock
方法
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
public void lock() {
sync.lock();
}
}
对于ReentrantLock
的Sync
的lock
方法。
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
}
Sync
的lock
方法,首先会通过compareAndSetState
来更新状态,更新成功,设置当前拥有锁的线程。没有更新成功说明锁已经被其他线程更新了,调用acquire(1)
,前面说过acquire
会调用tryAcquire
方法。
static final class NonfairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
直接调用父类的nonfairTryAcquire
方法。
abstract static class Sync extends AbstractQueuedSynchronizer {
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
传递的acquires为1,首先获取状态,再次尝试更新状态。无法更新状态时候,判断当前的线程是否为拥有锁的线程,是就更新状态,这个是重入,记录当前获取锁的次数。
总结:从上面可知,锁的状态有两种,一种是0表示没有线程持有,一种大于等于1表示锁已经被持有。并且通过setState更新重入次数。
unLock方法
public class ReentrantLock implements Lock, java.io.Serializable {
public void unlock() {
sync.release(1);
}
}
直接调用NonfairSync
的release
方法。而我们知道release
方法会调用到tryRelease
。会调用到NonfairSync
直接使用父类Sync
的tryRelease
方法。
abstract static class Sync extends AbstractQueuedSynchronizer {
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}
Sync
当前的状态减去releases
,这是因为我们一步步去减少重入。然后判断当前线程和用于锁的线程是不是同一个线程,不是同一线程抛出异常,这就是为什么获取的锁和释放锁需要在同一线程,不然就等着报错吧。当状态为0了,就说明锁没被持有,设置当前拥有锁的线程为null。
实现自己的独占锁
实现互斥锁,需要复写AbstractQueuedSynchronizer
的tryAcquire
和tryRelease
方法。我们假设有两种状态,0表示未被持有,1表示已经被持有。
public class MyLock {
private Sync mSync;
public MyLock(Sync sync) {
mSync = sync;
}
private class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
int state = getState();
if (state == 0) {
if (compareAndSetState(0, 1)) {
return true;
}
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
int state = getState();
if (state == 1) {
if (compareAndSetState(1, 0)) {
return true;
}
}
return false;
}
}
public void lock(){
mSync.acquire(1);
}
public void unLock(){
mSync.release(1);
}
}
可能这个类只是演示,它不够完善,比如超时等待,InterruptedException响应的lock等等,但是原理已经有了。
对于状态控制的类,我们可以使用AQS实现自己的类。