:独占式锁)
1、AQS属性以前文章提到过看一个类的属性这个类整个的功能。AQS中的属性public abstract class AbstractQueuedSynchronizer{ private transient volatile Node head; private transient volatile Node tail; private volatile int state; }state取值//节点从同步队列中取消 int CANCELLED 1 //后继节点的线程处于等待状态如果当前节点释放同步状态会通知后继节点使得后继节点的线程能够运行 int SIGNAL -1 //当前节点进入等待队列中 int CONDITION -2 //表示下一次共享式同步状态获取将会无条件传播下去 int PROPAGATE -3 //初始状态 int INITIAL 0;AQS的静态内部类Nodepublic abstract class AbstractQueuedSynchronizer{ static final class Node { //标记表示节点正在共享模式中等待 static final Node SHARED new Node(); //标记表示节点正在独占模式下等待 static final Node EXCLUSIVE null; //节点状态 volatile int waitStatus //当前节点/线程的前驱节点 volatile Node prev //当前节点/线程的后继节点 volatile Node next; //加入同步队列的线程引用 volatile Thread thread; //等待队列中的下一个节点和Condition配合使用 Node nextWaiter; } }剩下的方法都是围绕以上属性所服务的AQS提供的主要方法getState():返回同步状态的当前值setState(int newState):设置当前同步状态compareAndSetState(int expect, int update):使用CAS设置当前状态该方法能够保证状态设置的原子性isHeldExclusively():当前同步器是否在独占式模式下被线程占用一般该方法表示是否被当前线程独占acquire(int arg):独占式获取同步状态如果当前线程获取同步状态成功则由改方法返回否则将会进入同步队列等待该方法将会调用可重写的tryAcquire(int arg)方法acquireInterruptibly(int arg):与acquire(int arg)相同但是该方法响应中断当前线程为获取到同步状态而进入到同步队列中如果当前线程被中断则该方法抛出InterruptedException异常并返回acquireShared(int arg)共享式获取同步状态如果当前线程未获取到同步状态将会进入同步队列等待与独占式的主要区别是在同一时刻可以有多个线程获取到同步状态acquireSharedInterruptibly(int arg)共享式获取同步状态响应中断release(int arg)独占式释放同步状态该方法会在释放同步状态之后将同步队列中第一个节点包含的线程唤醒releaseShared(int arg)共享式释放同步状态tryAcquire(int arg):独占式获取同步状态获取同步状态成功后其他线程需要等待该线程释放同步状态才能获取同步状态tryRelease(int arg):独占式释放同步状态tryAcquireNanos(int arg, long nanos):超时获取同步状态如果当前线程在nanos时间内没有获取到同步状态那么将会返回false已经获取则返回truetryAcquireShared(int arg):共享式获取同步状态返回值大于等于0表示获取成功否则获取失败tryReleaseShared(int arg):共享式释放同步状态tryAcquireSharedNanos(int arg, long nanosTimeout)共享式获取同步状态增加超时限制2、独占式锁2.1、获取独占式锁调用lock()方法是获取独占式锁获取失败就将当前线程加入同步队列成功则线程执行。而lock()方法实际上会调用AQS的acquire()方法acquire根据当前获得同步状态成功与否做了两件事情成功则方法结束返回失败则先调用addWaiter()将线程加入等待队列,然后在调用acquireQueued()方法:排队再次获取锁。public final void acquire(int arg) { //获取同步状态成功则直接返回。若失败则先调用addWaiter()方法再调用acquireQueued()方法 if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } }2.2、获取独占式锁态失败:做入队操作private Node addWaiter(Node mode) { // 1. 将当前线程构建成Node Node node new Node(Thread.currentThread(), mode); // 2. 当前尾节点是否为null Node pred tail; if (pred ! null) { // 当前同步队列尾节点不为null则将当前节点尾插入的方式插入同步队列中 node.prev pred; if (compareAndSetTail(pred, node)) { pred.next node; return node; } } // 当前同步队列尾节点为null说明当前线程是第一个加入同步队列进行等待的线程 enq(node); return node; } private Node enq(final Node node) { //自旋的过程for (;;) for (;;) { Node t tail; if (t null) { //1. 构造头结点 if (compareAndSetHead(new Node())) tail head; } else { // 2. CAS操作设置尾节点CAS操作失败后负责自旋进行尝试会在for (;;)for死循环中不断尝试直至成功return返回为止。 node.prev t; if (compareAndSetTail(t, node)) { t.next node; return t; } } } }在同步队列中的节点线程会做什么事情了来保证自己能够有机会获得独占式锁了自旋acquireQueued2.3、入队列后线程排队获取独占式锁acquireQueuedfinal boolean acquireQueued(final Node node, int arg) { boolean failed true; try { boolean interrupted false; for (;;) { // 获取当前节点的先驱节点 final Node p node.predecessor(); // 2.1 如果当前节点的先驱节点是头结点并且成功获取同步状态即可以获得独占式锁 if (p head tryAcquire(arg)) { //获取锁成功出队操作队列头指针用指向当前节点 setHead(node); //释放前驱节点 p.next null; failed false; return interrupted; } // 2.2 获取锁失败线程进入等待状态等待获取独占式锁 if (shouldParkAfterFailedAcquire(p, node) parkAndCheckInterrupt()) //如果等待过程中被中断过就将interrupted标记为true interrupted true; } } finally { if (failed) cancelAcquire(node); } } //做出队操作 private void setHead(Node node) { head node; node.thread null; node.prev null; } //就进入waiting状态直到被unpark() private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //拿到前驱的状态 int ws pred.waitStatus; if (ws Node.SIGNAL) //如果已经告诉前驱拿完号后通知自己一下那就可以安心休息了 return true; if (ws 0) { /* * 如果前驱放弃了那就一直往前找直到找到最近一个正常等待的状态并排在它的后边。 * 注意那些放弃的结点由于被自己“加塞”到它们前边它们相当于形成一个无引用链稍后就会被GC回收 */ do { node.prev pred pred.prev; } while (pred.waitStatus 0); pred.next node; } else { //如果前驱正常那就把前驱的状态设置成SIGNAL告诉它拿完号后通知自己一下。有可能失败人家说不定刚刚释放完呢 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } private final boolean parkAndCheckInterrupt() { //调用park()使线程进入waiting状态 LockSupport.park(this); //如果被唤醒查看自己是不是被中断的 return Thread.interrupted(); }2.4、释放独占式锁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; } //释放锁 protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); } //唤醒等待队列里的下一个线程 private void unparkSuccessor(Node node) { //当前线程所在的结点状态 int ws node.waitStatus; if (ws 0)//置零当前线程所在的结点状态允许失败。 compareAndSetWaitStatus(node, ws, 0); Node s node.next;//找到下一个需要唤醒的结点后继节点 if (s null || s.waitStatus 0) {//如果后继节点为空或已取消 s null; for (Node t tail; t ! null t ! node; t t.prev) if (t.waitStatus 0)//从这里可以看出0的结点都是还有效的结点。 s t; } if (s ! null) LockSupport.unpark(s.thread);//唤醒 }