概述
AQS框架下的锁都是实现Lock接口并实现tryAcquire方法,在tryAcquire方法中对state变量进行修改来改变锁的状态。
- 重入性。获得锁的线程可以再次获得锁。
- 公平锁。严格保证先尝试获得锁的线程能够先获得锁。
- 非公平锁。不能严格保证先尝试获得锁的线程先获得锁。
获得锁
非公平锁
cas修改state的状态,如果成功设置当前重入锁的拥有者是当前线程。cas对当前state的期待值为0,所以一个线程如果是重入自己持有的锁,在这一步是无法成功的。
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
如果cas失败会调用acquire(1),即调用tryAcquire(1),最终调用的是nonfairAcquire(1),该方法是非公平锁的核心实现。
- 如果锁的state=0,cas修改,如果成功代表获得锁成功返回true;失败返回false,进入AQS的队列。
- 如果state!=0但当前锁的线程拥有者是当前线程,进入锁的重入阶段,修改锁的state,记录所的重入次数,返回true代表线程成功获得锁。
非公平重入锁的tryAcquire实现相对简单
- 在锁的内部记录锁的持有线程,方便锁的重入
- 用state的值记录锁的重入次数
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
/** * 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; }
公平锁
公平锁的lock方法中直接调用了acquire,而非公平锁lock方法中先cas,cas失败后才调用的acquire。这是公平性和非公平性的体现,acquire失败后会进入队列,AQS队列中的线程只有pre是head才可以尝试获得锁,即aqs通过队列的方式维护了公平性,如果把所有锁的获得都交给aqs去管理,那么锁就是公平的。相应的如果在加入aqs之前先自己尝试获取锁,那么这个锁就未必是公平的。例如:A线程获取锁的时候发现state不是0,失败进入AQS队列,B线程尝试获取锁的时候state恰好变成0,由于A线程在AQS队列尾部,A线程不具有争取锁的资格,所以B线程获得锁A没有获得锁,这就是非公平性的体现。
final void lock() { acquire(1); }
这里同样分为两个部分
- state==0代表锁空闲,但是要先判断AQS里是否有前继节点,如果有代表已经有人在排队,返回false进入AQS排队
- 如果锁的持有着是当前线程,则重入并修改state
/** * 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; }
课代表划重点非公平锁和公平锁
-
实现
- 非公平锁在lock方法中会先进行一次cas,cas成功当前线程就获取到锁,失败后才调用tryAcquire。公平锁没有先调用cas,而是调用acquire即调用tryAcquire。
- 在tryAcquire中,非公平锁只要当前state=0就可以进行cas,公平锁在state=0的时候还要先判断队列里是否有等待的线程,只有队列中没有等待的线程的情况下才会cas。
- 无论是公平锁还是非公平锁,才tryAcquire失败进入AQS队列后,AQS的先入先出的特性能够保证后续的公平性。
-
比较
- 非公平锁容易出现饥饿。一个刚刚释放锁的线程再次获取锁的概率很大,所以会出现一个线程连续多次获得锁,造成饥饿。
- 公平锁效率低。公平性需要线程切换从而有许多没有必要的切换。
释放锁
公平锁和非公平锁的释放没有区别,都是静态内部类sync中的tryRelease方法。
- 如果当前线程不是锁的持有线程,抛出异常。
- 由于释放锁的线程一定是持有锁的线程,所以释放锁修改state不需要cas,直接相减。
- 如果释放完毕后state=0,返回true,并设置当前所的持有者为null。
- 如果释放完毕后state=0,返回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; }
其余获得锁的方法
这些获得锁的方法都是公平锁和非公平锁没有区别。
lockInterruptibly
在获得锁失败被park时候能够相应中断,实现比较简单,直接调用AQS里的方法。
tryLock
非阻塞获取锁,类似NIO。如果lock没有被别的线程持有,那么tryLock会立即返回true,这种行为其实是一种非公平的锁,另外从它调用的方法nonfairTryAcquire也可以看出这一点。这也就意味着如果你申请了一个公平锁,调用一个公平锁的tryLock它的行为也是非公平的。如果lock此时被别的线程持有,tryLock会返回false。
public boolean tryLock() { return sync.nonfairTryAcquire(1); }
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; }