AQS之共享锁实现原理
一:概念
共享式获取与独占式获取最主要的区别在于同一时刻能否有多个线程同时 获取到同步状态。以读写为例,如果一个程序在进行读操作,那么这一时刻写操 作均被阻塞,
而读操作能够同时进行。写操作要求对资源的独占式访问,而读操 作可以是共享式访问。
二:通过countDownLatch计数器的使用来分析
共享锁的实现原理
定义一个计数器,初始计数值为5:
还是通过state进行标记的,只不过独占锁state表示线程的重入次数,而共享锁表示计数器的次数
调用await方法,则该线程将被阻塞
这里面有个尝试获取共享锁,如果当前计数器为0,则返回1,表示可以拿到共享锁,否则返回-1,表示不可以拿到。
如果返回1,则当前线程继续向下执行,不会被阻塞。如果为-1 则执行
doAcquireSharedInterruptibly
这里也是入对列,创建一个节点,如果当前节点的前驱节点正在执行,则尝试获取共享锁,获取到锁则传播唤醒下个线程,
获取不到则进入对列阻塞,等待被唤醒。
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
将当前节点设置为head,并且释放共享锁:
private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below setHead(node); if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next; if (s == null || s.isShared()) doReleaseShared(); } }
因为是共享锁,所以当一个线程拿到共享锁之后,会唤醒后续线程,然后后续线程再唤醒下一个节点。
来看一下countDown方法的实现:
如果countDownlatch的state减为0,则调用
doReleaseShared方法,释放阻塞的线程。