什么是公平锁?
加锁的时候会去判断等待队列中是否有线程在等待,有则进入排队(不一定睡眠),无则去获取锁
源码解析
FairSync 公平锁源码
static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;//加锁final void lock() {acquire(1);}//尝试加锁protected final boolean tryAcquire(int acquires) {//获取当前线程final Thread current = Thread.currentThread();//获取当前锁状态int c = getState();//如果锁未被持有if (c == 0) {//hasQueuedPredecessors() 查询队列中是否有等待的线程//compareAndSetState() 尝试修改锁状态if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {//如果没有队列且修改锁状态成功,则将占用锁的线程设置为当前线程setExclusiveOwnerThread(current);return true;}}//判断是否重入锁(是否是同一个线程加锁两次)else if (current == getExclusiveOwnerThread()) {//锁状态值+1int nextc = c + acquires;//锁状态值小于0则抛出异常if (nextc < 0)throw new Error("Maximum lock count exceeded");//重新设置锁状态值setState(nextc);return true;}return false;}}
lock()方法调用了acquire ()方法
acquire 获取锁方法
public final void acquire(int arg) {//tryAcquire再次尝试加锁//addWaiter,将线程加入到队列中//acquireQueued,判断队列(链表)的上一个元素是否是头部,如果是自旋去获取锁,如果不是则进入park状态if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
tryAcquire 在上面的FairSync 中,如果为tryAcquire 方法中加锁成功,返回true,那么! true = false,就不会走acquireQueued()方法。如果没加锁成功则先运行addWaiter()方法,再运行acquireQueued()方法得到整个if表达式的最终结果
- addWaiter 将当前线程添加到队列中,返回node
private Node addWaiter(Node mode) {//根据当前线程创建node节点,这里的node节点其实是双向队列Node node = new Node(Thread.currentThread(), mode);// 获取队尾Node pred = tail;if (pred != null) {//队尾不为空,快速尝试在队尾添加node//先将当前节点的上一个指向获取到的队尾元素node.prev = pred;//尝试将队尾设置为nodeif (compareAndSetTail(pred, node)) {//如果CAS成功,就说明没有别的线程设置队尾成功//只需要将之前的队尾的next指向当前nodepred.next = node;return node;}}enq(node);return node;}
