1.整体调用结构
默认情况下是非公平锁:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
对于加锁、解锁、可中断锁、超时加锁、抢占非阻塞式加锁(tryLock(),即使在公平模式下也是抢占式而非排队),最终都转交给了sync来处理。
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//该加锁为抢占式加锁,在公平模式下也是抢占式,其不会阻塞
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
2.可重入是怎么实现的?
2.1 先看看非公平锁的lock
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
acquire(1)调用的是AQS的:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
其中tryAcquire(arg)调用的是NonfairSync的:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
nonfairTryAcquire(acquires)是其父类Sync中的final方法:
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;
}
关键就在该方法里面,current == getExclusiveOwnerThread()时不会阻塞,而是会记录当前线程加锁的次数并更新到state中去。
2.2 再看看公平锁
final void lock() {
acquire(1);
}
与非公平锁一样调用AQS的acquire,然后acquire会调用FairSync.tryAcquire:
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;
}
同理,有一模一样的处理方式:current == getExclusiveOwnerThread()时会更新该线程加锁的次数。
对于其他的中断锁、超时锁等,都会调用公平锁、非公平锁的tryAcquire(其中断机制和超时机制由AQS已经完全实现了),而tryAcquire中加锁时都会处理可重入的实现。
2.3 释放锁
public void unlock() {
sync.release(1);
}
调用AQS的release:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
AQS的release会调用Sync.tryRelease final方法:
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;
}
可以看出如果线程加锁了n次,必须解锁n次才能真正释放锁。
3.公平锁与非公平锁的区别
从上面可以看出释放锁时公平锁和非公平锁都一样,因此公平和非公平是针对获取锁而言的,具体地讲,是tryAcquire的不同。
非公平锁的tryAcquire:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
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;
}
非公平锁,会有一个CAS抢占过程,如果抢占成功,加成功获取锁,而非按照FIFO模式获取锁。
公平锁的tryAcquire:
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;
}
/**
* Queries whether any threads have been waiting to acquire longer
* than the current thread.
* @return {@code true} if there is a queued thread preceding the
* current thread, and {@code false} if the current thread
* is at the head of the queue or the queue is empty
* @since 1.7
*/
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
如果队列中有线程在等待,则不会获取锁,也即会加入到同步队列中进行等待。
公平锁保证了锁获取按照FIFO原则,代价是进行大量的线程切换。非公平锁虽然造成线程饥饿,但极少的线程切换(刚释放锁的线程再次获得锁的几率非常大),保证了其更大的吞吐量。
网友评论