一文带你学会AQS和并发工具类的关系2

1.创建公平锁 1.使用方式 Lock reentrantLock = new ReentrantLock(true); reentrantLock.lock(); //加锁 try{ // todo } finally{ reentrantLock.unlock(); // 释放锁 } 2.创建公平锁

在new ReentrantLock(true)的时候加入关键字true

public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }

当传入的参数值为true的时候创建的对象为new FairSync()公平锁。

2.加锁的实现 1.普通的获取锁 reentrantLock.lock(); //加锁

加锁的实际调用的方法是创建的公平锁里面的lock方法

图片

static final class FairSync extends Sync { final void lock() { acquire(1); } ... }

代码中的acquire方法和非公平锁中的acquire方法一样都是调用的AQS中的final方法

## AbstractQueuedSynchronizer public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }

不过不同之处是这里面的tryAcquire(arg)方法是调用的公平锁里面实现的方法

图片

这个方法其实和非公平锁方法特别相似,只有一处不同公平锁中含有一个特殊的方法叫做hasQueuedPredecessors()该方法也是AQS中的方法,该方法的实质就是要判断该节点的前驱节点是否是head节点

## AbstractQueuedSynchronizer public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }

剩下的部分和前一篇分析的非公平锁几乎是一个流程

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; } } // 如果是当前线程持有的锁信息,在原来的state的值上加上acquires的值 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); // 设置state的值 setState(nextc); // 获取锁成功 return true; } // 获取锁失败了才返回false return false; }

注意一下只有当返回false的时候才是tryAcquire失败的时候。此时就会走到繁琐的addWaiter(Node.EXCLUSIVE)方法

2.普通获取锁失败

如果前面tryAcquire失败就会进行接下来的addWaiter(Node.EXCLUSIVE)

## AbstractQueuedSynchronizer private Node addWaiter(Node mode) { // 创建一个新的node节点 mode 为Node.EXCLUSIVE = null Node node = new Node(Thread.currentThread(), mode); // 获取尾部节点 Node pred = tail; // 如果尾部节点不为空的话将新加入的节点设置成尾节点并返回当前node节点 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } // 如果尾部节点为空整明当前队列是空值得需要将当前节点入队的时候先初始化队列 enq(node); return node; } 3.节点入队方法

enq(node)方法是节点入队的方法我们来分析一下,enq入队方法也是AQS中的方法,注意该方法的死循环,无论如何也要将该节点加入到队列中。

## AbstractQueuedSynchronizer private Node enq(final Node node) { for (;;) { Node t = tail; // 如果尾节点为空的话,那么需要插入一个新的节点当头节点 if (t == null) { if (compareAndSetHead(new Node())) tail = head; } else { // 如果不为空的话,将当前节点变为尾节点并返回当前节点的前驱节点 node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }

其实和非公平锁的addWaiter(Node node)是一样的流程,分析完。

4.acquireQueued方法

此时当前节点已经被加入到了阻塞队列中了,进入到了acquireQueued方法。该方法也是AQS中的方法。

## AbstractQueuedSynchronizer final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 获取当前节点的前驱节点 final Node p = node.predecessor(); // 如果当前节点的前驱节点是头节点的话会再一次执行tryAcquire方法获 // 取锁 if (p == head && tryAcquire(arg)) { // 设置当前节点为头节点 setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }

注意 setHead(node) 中具体的实现细节thread为null,prev也为null其实就是如果当前节点的前驱节点为头节点的话,那么当前节点变成了头节点也就是之前阻塞队列的虚拟头节点。

private void setHead(Node node) { head = node; node.thread = null; node.prev = null; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspywg.html