ReentrantLock分析

匿名 (未验证) 提交于 2019-12-03 00:03:02

主要分析下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的条件,即先进入队列的线程会先获取锁。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!