描述
本文使用ReentrantLock
和CountDownLatch
演示獨占鎖和共享鎖的實現(xiàn)误债。
AQS屬性
Node head
Node tail
int status
Thread exclusiveOwnerThread
AQS內部Node屬性
int waitStatus
Node prev
Node next
Thread thread
Node nextWaiter
獨占鎖
獲取鎖
- 1線程先獲取鎖,設置
status
和exclusiveOwnerThread
辉哥。1線程獲取鎖 - 2線程去獲取鎖吞加,發(fā)現(xiàn)鎖已被使用灼芭,只能加入等待隊列。隊列先初始化一個head節(jié)點,然后將2線程封裝成Node碰酝,拼接到head的next節(jié)點上全庸,也就是tail節(jié)點纷宇。
- 3線程去獲取鎖,也加入等待隊列顷编。將3線程封裝成Node插入到隊列尾部暖释,也就是tail袭厂。
釋放鎖
- 1線程執(zhí)行完,設置status和exclusiveOwnerThread為初始值球匕,釋放鎖纹磺。并叫醒head的next節(jié)點。
- 2線程被喚醒亮曹,設置status和exclusiveOwnerThread橄杨。并將自己所在的Node的thread和prev設置為null,提升為head
共享鎖
通過status標識鎖
獨占鎖
ReentrantLock使用排他鎖照卦。AQS的status>0表示加鎖式矫,thread是當前獲取鎖的線程。該鎖時可重入鎖役耕,所以status>0采转。
public static void main(String[] args) {
final ExecutorService pool = Executors.newFixedThreadPool(3);
final X x = new X();
for (int i = 0; i < 3; i++) {
pool.execute(x::m);
}
pool.shutdown();
}
static class X {
private final ReentrantLock lock = new ReentrantLock();
private int i;
void m() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + i++);
} finally {
lock.unlock();
}
}
}
共享鎖
CountDownLatch
使用共享鎖。AQS的status為共享鎖的標記位瞬痘,status>0就是加鎖故慈,等于0就是釋放鎖。每調用一次countDown()框全,status減1察绷。
線程會阻塞在await(),直到countDown()將status置為0
public static void main(String[] args) {
final int down = 3;
final CountDownLatch start = new CountDownLatch(1);
final CountDownLatch count = new CountDownLatch(down);
final ExecutorService pool = Executors.newFixedThreadPool(down);
for (int i = 0; i < down; i++) {
pool.execute(new WorkerRunnable(count, start, i));
}
count.countDown();
pool.shutdown();
}
static class WorkerRunnable implements Runnable {
private final CountDownLatch doneSignal;
private final CountDownLatch startSignal;
private final int i;
WorkerRunnable(CountDownLatch doneSignal, CountDownLatch startSignal, int i) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
this.i = i;
}
public void run() {
try {
startSignal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
doneSignal.countDown();
}
}