從使用角度看 ReentrantLock 和 Condition
阿新 • • 發佈:2018-06-03
阻塞 transient string turn his 介紹 ner await dex
java 語言中談到鎖,少不了比較一番 synchronized 和 ReentrantLock 的原理,本文不作分析,只是簡單介紹一下 ReentrantLock 的用法,從使用中推測其內部的一些原理。
代碼示例:
public static void main(String[] args) throws InterruptedException { final ReentrantLock lock = new ReentrantLock(); final Condition con1 = lock.newCondition(); finalCondition con2 = lock.newCondition(); // lock.lock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { lock.lock(); con1.await(); System.out.println("wake from cond1"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Overridepublic void run() { try { lock.lock(); con2.await(); System.out.println("wake from cond2"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); t2.start(); Thread t3 = new Thread(new Runnable() { @Override public void run() { try { lock.lock(); con2.await(); System.out.println("wake from cond2"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); t3.start(); Thread.sleep(100); try { lock.lock(); System.out.println("lock.getQueueLength() = " + lock.getQueueLength()); System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1)); System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2)); con1.signal(); con2.signalAll(); Thread.sleep(100); System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1)); System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2)); } finally { lock.unlock(); } }
以 ReentrantLock.getQueueLength 和 ReentrantLock.getWaitQueueLength 作為入口:前者作用是返回獲取鎖失敗,處於等待狀態的線程個數,後者是返回等待某一個 condition 的線程個數。
// int java.util.concurrent.locks.ReentrantLock.getQueueLength() public final int getQueueLength() { return sync.getQueueLength(); } // int java.util.concurrent.locks.AbstractQueuedSynchronizer.getQueueLength() public final int getQueueLength() { int n = 0; for (Node p = tail; p != null; p = p.prev) { if (p.thread != null) ++n; } return n; }
很簡單,就是遍歷阻塞線程的鏈表。
// int java.util.concurrent.locks.ReentrantLock.getWaitQueueLength(Condition condition) public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition); } // int java.util.concurrent.locks.AbstractQueuedSynchronizer.getWaitQueueLength(ConditionObject condition) public final int getWaitQueueLength(ConditionObject condition) { if (!owns(condition)) throw new IllegalArgumentException("Not owner"); return condition.getWaitQueueLength(); } // int java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.getWaitQueueLength() protected final int getWaitQueueLength() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); int n = 0; for (Node w = firstWaiter; w != null; w = w.nextWaiter) { if (w.waitStatus == Node.CONDITION) ++n; } return n; }
同樣,也是遍歷鏈表,不同的是,這是 Condition 的鏈表。
現在,我們知道,ReentrantLock 中有 2 種不同的鏈表,其一是阻塞線程,其二是 Condition 等待鏈表,2 中鏈表都是使用 Node:
// java.util.concurrent.locks.AbstractQueuedSynchronizer.Node static final class Node { static final Node EXCLUSIVE = null; // 線程阻塞節點的狀態 static final int CANCELLED = 1; static final int SIGNAL = -1; // condition 節點的狀態 static final int CONDITION = -2; static final int PROPAGATE = -3; volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; }
節點的類型表明鏈表的類型。
看看 Condition 實現類的 api:
public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; private transient Node firstWaiter; private transient Node lastWaiter; /** * Adds a new waiter to wait queue. * @return its new wait node */ private Node addConditionWaiter() { Node t = lastWaiter; // If lastWaiter is cancelled, clean out. if (t != null && t.waitStatus != Node.CONDITION) { unlinkCancelledWaiters(); t = lastWaiter; } Node node = new Node(Thread.currentThread(), Node.CONDITION); if (t == null) firstWaiter = node; else t.nextWaiter = node; lastWaiter = node; return node; } public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) { LockSupport.park(this); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); } /** * Moves the longest-waiting thread, if one exists, from the * wait queue for this condition to the wait queue for the * owning lock. * * @throws IllegalMonitorStateException if {@link #isHeldExclusively} * returns {@code false} */ public final void signal() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignal(first); } }
在開頭的示例中,創建了 2 個 Condition 對象,每個Condition 對象有一個自己的等待鏈表。
從使用角度看 ReentrantLock 和 Condition