AbstractQueuedSynchronizer
AbstractQueuedSynchronizer是Java并发工具包中最重要的工具,它是一个抽象类,为Java的各种同步器,锁等提供了并发抽象,是由大名鼎鼎的Doug Lea完成。
java.util.concurrent提供了很多并发工具类,其中很多都是基于AbstractQueuedSynchronizer实现的。如,ReentrantLock、ReentrantReadWriteLock、CountDownLatch、CyclicBarrier【CyclicBarrier内部使用ReentrantLock和Condition实现,间接使用了AbstractQueuedSynchronizer】、Semaphore、各种BlockingQueue【与CyclicBarrier类似,内部使用了ReentrantLock】、ConcurrentHashMap【与CyclicBarrier类似,内部使用了ReentrantLock】、CopyOnWriteArrayList【与CyclicBarrier类似,内部使用了ReentrantLock】等。由此可以看出AbstractQueuedSynchronizer的重要性。
AbstractQueuedSynchronizer内部使用了compareAndSwap(CAS)无锁操作来实现锁的,compareAndSwap()是使用native方法实现的,其提供了一个原子语义上的操作,即要更新一个变量的值时,如果该变量的原始值是我认为的原始值,那么我就更新成功,否则就更新失败,更新失败就一直尝试更新,若干次之后总会更新成功。而不是像synchronized那样从操作系统层面上加锁,因此引入AbstractQueuedSynchronizer之后大大优化了Java的锁性能。
既然java.util.concurrent中这么多并发工具选择扩展AbstractQueuedSynchronizer来实现锁、条件队列和条件谓词,那么,如何使用AbstractQueuedSynchronizer来实现我们自己的同步工具呢?
如果要扩展AbstractQueuedSynchronizer,强烈建议使用组合而非继承进行扩展,可以在自己的同步工具内部维持一个私有的AQS的子类,
这个AbstractQueuedSynchronizer的子类只需覆盖AbstractQueuedSynchronizer中的【当只支持独占操作时,即排它锁】
- tryAcquire(int);
- tryRelease(int);
- isHeldExclusively()方法【tryAcquire返回true时表示设置state成功,获取到锁了,返回false表示没获取到】;
或者覆盖AbstractQueuedSynchronizer中的【当支持共享获取时,即共享锁】,
- tryAcquireShared(int);
- tryReleaseShared(int)【tryAcquireShared返回值小于0时表示没获取到锁,等于0表示获取到锁但是后续线程获取不到锁,大于0表示获取到锁&后续线程可能也会获取到锁】;
这些方法在AbstractQueuedSynchronizer中直接是throw new UnsupportedOperationException()。
在自己的工具类中使用子类继承AbstractQueuedSynchronizer时,直接实现这些方法,然后就可以使用这个子类的
- acquire(int);
- release(int);
- acquireShared(int);
- releaseShared(int);
来获取锁了。注意:AbstractQueuedSynchronizer中acquire(int)、release(int)、acquireShared(int)、releaseShared(int)都会调用子类中【因为被覆盖了】带有前缀try的版本来判断某个操作是否能执行(比如acquire(int)内部其实是会调用tryAcquire(int)来尝试获取锁,而tryAcquire(int)是AbstractQueuedSynchronizer本身就实现好的),所以我们只需要实现有前缀try的获取许可的方法版本。
ReentrantLock
ReentrantLock内部使用了AbstractQueuedSynchronizer来实现锁。其内部使用Sync扩展了AbstractQueuedSynchronizer。
如ReentrantLock中的Sync类:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
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;
}
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;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
其中,ReentrantLock内部的非公平锁和公平锁实现略有差别:
非公平锁为:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平锁:
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
然后调用NonFairSync或FairSync的lock()方法就可以获取到锁了。
由上可知,我们其实只需要实现AbstractQueuedSynchronizer中的try前缀方法就好了,然后我们就可以自由的通过acquire()或者release()等方法来获取锁和释放锁。
Semaphore
与ReentrantLock一样,Semaphore内部使用了AbstractQueuedSynchronizer来实现锁。其内部使用Sync扩展了AbstractQueuedSynchronizer。
Semaphore中的Sync类:
/**
* Synchronization implementation for semaphore. Uses AQS state
* to represent permits. Subclassed into fair and nonfair
* versions.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
其中,非公平信号量和公平信号量实现略有差别:
非公平信号量:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
公平信号量:
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
然后调用NonFairSync或者FairSync的acquireShared(int)方法就可以获取到信号量了。
自定义简单的AbstractQueuedSynchronizer实现类:
首先,扩展AbstractQueuedSynchronizer类,
class MyLock {
private Sync sync;
private static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 3633529035712707665L;
@Override
public boolean tryAcquire(int acqiure) {
return compareAndSetState(0, 1); //不是可重入的
}
@Override
public boolean tryRelease(int acqiure) {
setState(0); // 不是可重入的
return true;
}
}
MyLock() { this.sync = new Sync(); }
public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
}
其次,构建线程使用自己实现的同步工具,
class SyncThread implements Runnable {
private MyLock lock;
public SyncThread(MyLock lock) {
this.lock = lock;
}
public void run() {
lock.lock();
for (int i = 0; i < 6; i ++) {
System.out.print(ConcurrentTest.i++ + " ");
}
lock.unlock();
}
}
最后,测试自己实现的同步工具,
public class ConcurrentTest {
public static int i = 1;
public static void main(String[] args) {
MyLock lock = new MyLock();
ExecutorService exec = new ThreadPoolExecutor(3, 1000, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2000), new ThreadPoolExecutor.CallerRunsPolicy());
exec.execute(new SyncThread(lock));
exec.execute(new SyncThread(lock));
exec.execute(new SyncThread(lock));
}
}
输出为,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
总结
AbstractQueuedSynchronizer是Java中非常基础并且非常重要的抽象同步器。Java中诸多同步工具扩展了AbstractQueuedSynchronizer来实现锁、条件队列和条件谓词。我们也可以自己扩展AbstractQueuedSynchronizer来实现锁等。不过在工作中基本不会自己来实现AbstractQueuedSynchronizer,因为Java提供的ReentrantLock等等工具已经能够基本满足开发需求。
喜欢的可以关注微信公众号: