在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock、ReentrantReadWriteLock,其实现都依赖java.util.concurrent.AbstractQueuedSynchronizer类,实现思路都大同小异。
同时,Lock提供了Condition接口,类似与Object的wait/notify,但比后者要方便的多、灵活的多。
ReentrantLock的调用过程
ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,该类继承了AbstractQueuedSynchronizer。
static abstract class Sync extends AbstractQueuedSynchronizer
Sync又有两个子类:
final static class NonfairSync extends Sync
final static class FairSync extends Sync
Reentrant.lock()方法的调用过程(默认非公平锁):
AbstractQueuedSynchronizer通过构造一个基于阻塞的CLH队列容纳所有的阻塞线程,而对该队列的操作均通过Lock-Free(CAS)操作,但对已经获得锁的线程而言,ReentrantLock实现了偏向锁的功能。
非公平锁的代码中总是优先尝试当前是否有线程持有锁,一旦没有任何线程持有锁,那么非公平锁就霸道的尝试将锁“占为己有”。如果在抢占锁的时候失败就和公平锁一样老老实实的去请求。也即是说公平锁和非公平锁只是在入AQS的CLH队列之前有所差别,一旦进入了队列,所有线程都是按照队列中先来后到的顺序请求锁。
Lock类调用LockSupport,LockSupport进而调用Unsafe类让操作系统进行park和unpack,从而达到线程的阻塞和恢复。
ReentrantReadWriteLock类
适用于有很多线程都从某个数据结构中读取数据而很少有线程对其进行修改的场景,允许读取器线程共享访问,但与写入器线程依然是互斥的。
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private Lock readLock = rwl.readLock(); //得到一个可被多个读操作共用的读锁,但它会排斥所有写操作
private Lock writeLock = rwl.writeLock(); //得到一个写锁,它会排斥所有其他的读操作和写操作
public double getTotalBalance(){
readLock.lock();
try {
...
} finally {
readLock.unlock();
}
}
public void transfer(){
writeLock.lock();
try {
...
} finally {
writeLock.unlock();
}
}