ReentrantLock分析
主要分析下ReentrantLock锁的占用和释放过程。 一.几个核心变量AbstractOwnableSynchronizer{ /** * 表示当前占有独占锁的线程,为null时说明锁未被占用 */ private transient Thread exclusiveOwnerThread; } AbstractQueuedSynchronizer extend AbstractOwnableSynchronizer{ private transient volatile Node head;//队列首节点 private transient volatile Node tail;//队列尾节点 private volatile int state;//同步状态,表示锁是否被占用。可重入锁,占用锁时继续获取锁,state=2 } /** * waitStatus: *1:线程取消等待 *-1:后继节点的线程处于等待状态,需要当前结点唤醒 *-2:等待condition,condition.signale()唤醒,该线程会加入到队列中等待获取锁 *-3:下一次共享式同步状态获取将会被无条件地传播下去??没看懂 */ static final class Node { volatile int waitStatus;//当前线程的等待状态。状态被一个线程修改后,立即对其他线程可见 volatile Node prev;//前置节点 volatile Node next;//后置节点 volatile Thread thread;//节点所属线程 } AbstractQueuedSynchronizer同步控制核心类,核心变量为state,state=0表示当前锁被占用,state!=0表示锁被占用,exclusiveOwnerThread变量表示当前占用锁的线程,若为null,表示锁未被占用。 二.线程获取锁的流程1.尝试获取锁,若获取失败,则添加到待占用锁队列,中断当前线程等待占有锁后继续运行 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE),arg)) selfInterrupt(); } 2.获取失败的锁加入等待队列,第一个节点Node0为头节点,第二个节点Node1才是链表第一个个数据节点,存储有效数据信息。 private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(),mode); Node pred = tail; //尾节点存在,即队列不为空。新节点作为新的尾节点 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred,node)) { pred.next = node; return node; } } enq(node); return node; } //死循环添加节点,返回node的前置节点 private Node enq(final Node node) { for (;;) { Node t = tail; //尾节点不存在,初始化。设置一个前置节点node0,即为头节点也是尾节点 if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { //尾节点存在,即链表有效,将新node添加到尾部 node.prev = t; if (compareAndSetTail(t,node)) { t.next = node; return t; } } } } 3.死循环获取锁。死循环,只有前置节点为头节点的链表节点,即链表的第一个数据节点尝试获取锁 final boolean acquireQueued(final Node node,int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); //前置结点为头结点,即node为第一个,设置当前节点为头节点 if (p == head && tryAcquire(arg)) { //将当前节点设置为头结点,移除之前的头节点 setHead(node); p.next = null; // help GC。。p节点断开和链表的连接 failed = false; return interrupted; } //前置节点非首节点,则当前线程中断 if (shouldParkAfterFailedAcquire(p,node) && parkAndCheckInterrupt())//阻塞线程并判断线程是否中断 interrupted = true; } } finally { if (failed) cancelAcquire(node); } } 4.获取锁的具体流程 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //当前锁未被占用,且当前线程是队列中头元素Node1,如果是的话,则获取该锁,设置锁的状态,并设置锁的拥有者为当前线程 if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0,acquires)) { setExclusiveOwnerThread(current); return true; } } //若当前线程占有锁,锁可重入,state+1. else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } } 三.释放锁的流程1.尝试释放锁,锁释放成功,则唤醒下一个节点 public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; //若锁释放成功,则唤醒当前结点的后继结点 if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } 2.如何释放锁 每次释放state-1,并更新线程state值,直到state减到0,该线程释放锁成功。并将exclusiveOwnerThread字段置为null,表示当前未有线程占有锁 protected final boolean tryRelease(int releases) { //获取当前的state值,state-1 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; } 3.如何唤醒下一个结点 private void unparkSuccessor(Node node) { //当前结点的等待状态为置为0,Node1 int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node,ws,0); //从尾结点向前查找第一个waitStatus小于0的Node,Node2 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); } 四.公平锁和非公平锁ReentrantLock有可以作为公平锁和非公平锁。默认非公平锁。 public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 公平ReentrantLock锁获取锁 final void lock() { acquire(1); } 非公平ReentrantLock锁获取锁 final void lock() { if (compareAndSetState(0,1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } 二者的区别就是非公平锁获取锁的时候首先判断锁是否被占用,若没有被占用,直接占有锁,否则加入等待队列。 公平锁获取锁的时候的直接加入等待队列。等待队列的线程满足FIFO的条件,即先进入队列的线程会先获取锁。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |