至此,公平锁的加锁操作全部结束(或者已经取消获取锁),在同步等待队列中等待的线程,如果对应的节点的先驱节点不是头节点,则线程会被阻塞减少调度。
公平锁的公平的保证时靠加锁时判断当前锁对应的同步等待队列中存在等待的队列,如果有则当前线程入队,排队来保证的;
如果当前锁没有被获取,并且队列不存在或者队列中没有等待的线程则可以直接加锁。回到acquire()方法,如果在线程等等待的过程中发生了中断,
那么获取到所之后会还原中断。
通过加锁的过程可以发现,锁被获取的次数通过给state字段增加和设置锁所属的线程exclusiveOwnerThread来完成加锁操作,
那么当线程需要解锁的时候应该也是对这两个字段的操作,且解锁一定在加锁之后,因此不存在进入同步等待队列等待的过程。
解锁入口:unlock()方法;unlock()方法定义在ReentrantLock类里面,通过调用同步器的解锁操作完成解锁过程,公平机制不影响对外暴露的接口
代码具体解释见注释
release()方法,在AQS中实现
/** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument. This value is conveyed to * {@link #tryRelease} but is otherwise uninterpreted and * can represent anything you like. * @return the value returned from {@link #tryRelease} */ //释放持有的排他锁 public final boolean release(int arg) { //调用tryRelease()来释放锁,由同步器实现 //tryRelease方法的解释在同步器章节 //如果有线程在同步等待队列中等待获取锁, //那么还应该唤醒等待的线程 if (tryRelease(arg)) { //如果存在同步等待队列,那么当前节点解锁成功后回将自身节点设置为头节点 //因此这里的头节点就是自身当前线程的节点 //但是在加锁成功的时候会将节点的thread字段设置为null,因此无法比对判断 Node h = head; //后继线程在阻塞前以前会将前驱结点的waitStatus设置为SIGNAL=-1,因此不为0即需要唤醒后继节点 //为什么是不为0,而不是等于-1??因为node有1,-1,-2,-3和0五种情况 //0代表默认状态,node节点刚被创建, //1代表当前节点已经取消获取锁, //-1代表有后继节点需要唤醒 //-2代表节点在条件等待队列中等待,也就是不会出现在同步等待队列 //-3代表共享模式, //如果在获取到锁并且已经存在后继节点的时候取消获取锁,那么节点就会使1, //直接点线程被唤醒完成加锁操作后释放锁,他的waitStatus使1而不是-1,因此使用的是waitStatus != 0 if (h != null && h.waitStatus != 0) //唤醒后继节点 unparkSuccessor(h); return true; } return false; }unparkSuccessor()方法唤醒后继节点,在AQS中实现
/** * Wakes up node's successor, if one exists. * * @param node the node */ //唤醒头结点的后继节点 private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; //头节点的状态<0,则在发出唤醒信号之前尝试清除这个状态,即将头节点的状态设置为0, //允许失败或者被等待的线程改变 if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ 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) s = t; } //唤醒没有取消获取锁的第一个节点 if (s != null) LockSupport.unpark(s.thread); }至此可重入锁的公平模式的加锁和解锁全部结束