併發編程-AQS介紹和原理分析(上)

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"Lock模型","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/62/6253d0994cd5d037cd571069dde8b1fa.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"AQS(AbstractQuenedSynchronizer抽象隊列式同步器)","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"核心思想&基本框架","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果被請求的共享資源空閒,則將當前請求資源的線程設置爲有效的工作線程,並將共享資源設置爲鎖定狀態,如果被請求的共享資源被佔用,那麼就需要一套線程阻塞等待以及被喚醒時鎖分配的機制,這個機制AQS是用CLH隊列鎖實現的,即將暫時獲取不到鎖的線程加入到隊列中。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"AQS維護了一個volatile語義(支持多線程下的可見性)的共享資源變量state和一個FIFO線程等待隊列CLH(多線程競爭state被阻塞時會進入此隊列)。\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/56/56f21f37d6cc9930fda1d2ceb2ae56d9.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"state","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"共享資源變量state,三種訪問方式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"getState()","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"setState(int newState)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"compareAndSetState(int expect, int update)","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"資源共享的方式,兩種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"獨佔式(Exclusive)只有單個線程能夠成功獲取資源並執行,如ReentrantLock。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"共享式(Shared)多個線程可成功獲取資源並執行,如Semaphore/CountDownLatch等。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Node節點 - CLH(Craig, Landin, and Hagersten locks)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CLH鎖其實就是一種是基於邏輯隊列非線程飢餓的一種自旋公平鎖(","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"基於單向鏈表(隱式創建)的高性能、公平的自旋鎖","attrs":{}},{"type":"text","text":"),由於是 Craig、Landin 和 Hagersten三位大佬的發明,因此命名爲CLH鎖。AQS內部的FIFO線程等待隊列,通過內部類Node來實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"static final class Node {\n\n // 表明節點在共享模式下等待的標記\n static final Node SHARED = new Node();\n // 表明節點在獨佔模式下等待的標記\n static final Node EXCLUSIVE = null;\n\n // 表徵等待線程已取消的\n static final int CANCELLED = 1;\n // 表徵需要喚醒後續線程\n static final int SIGNAL = -1;\n // 表徵線程正在等待觸發條件(condition)\n static final int CONDITION = -2;\n // 表徵下一個acquireShared應無條件傳播\n static final int PROPAGATE = -3;\n\n /**\n * SIGNAL: 當前節點釋放state或者取消後,將通知後續節點競爭state。\n * CANCELLED: 線程因timeout和interrupt而放棄競爭state,當前節點將與state徹底拜拜\n * CONDITION: 表徵當前節點處於條件隊列中,它將不能用作同步隊列節點,直到其waitStatus被重置爲0\n * PROPAGATE: 表徵下一個acquireShared應無條件傳播\n * 0: None of the above\n */\n volatile int waitStatus;\n\n // 前繼節點\n volatile Node prev;\n // 後繼節點\n volatile Node next;\n // 持有的線程\n volatile Thread thread;\n // 鏈接下一個等待條件觸發的節點\n Node nextWaiter;\n\n // 返回節點是否處於Shared狀態下\n final boolean isShared() {\n return nextWaiter == SHARED;\n }\n\n // 返回前繼節點\n final Node predecessor() throws NullPointerException {\n Node p = prev;\n if (p == null)\n throw new NullPointerException();\n else\n return p;\n }\n\n // Shared模式下的Node構造函數\n Node() { \n }\n\n // 用於addWaiter\n Node(Thread thread, Node mode) { \n this.nextWaiter = mode;\n this.thread = thread;\n }\n\n // 用於Condition\n Node(Thread thread, int waitStatus) {\n this.waitStatus = waitStatus;\n this.thread = thread;\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"題外話:關於鎖的一些內容:簡單的非公平自旋鎖以及基於排隊的公平自旋鎖的實現 https://blog.csdn.net/dm_vincent/article/details/79677891CLH鎖的原理和實現 https://blog.csdn.net/dm_vincent/article/details/79842501","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面通過AbstractQuenedSynchronizer(同步器)和ReentrantLock(鎖)來詳細說明一下AQS的原理。除了共享和獨佔的特點外,重點關注它的三個特性:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"公平和非公平","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可中斷","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"條件(後續文章說明)","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"API","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同步器是實現鎖的關鍵,利用同步器將鎖的語義實現,然後在鎖的實現中聚合同步器。可以這樣理解:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鎖的API是面向使用者的,它定義了與鎖交互的公共行爲,而每個鎖需要完成特定的操作也是透過這些行爲來完成的(比如:可以允許兩個線程進行加鎖,排除兩個以上的線程),但是實現是依託給同步器來完成","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同步器面向的是線程訪問和資源控制,它定義了線程對資源是否能夠獲取以及線程的排隊等操作。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鎖和同步器很好的隔離了二者所需要關注的領域,嚴格意義上講,同步器可以適用於除了鎖以外的其他同步設施上(包括鎖)","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"AQS內部定義(實現)的方法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"獨佔式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"acquire(int)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"acquireInterruptibly(int)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tryAcquireNanos(int,long)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"release(int)","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"共享式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"acquireShared(int)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"acquireSharedInterruptibly(int)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tryAcquireSharedNanos(int,long)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"releaseShared(int)","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"需要繼承的鎖自定義實現的方法","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tryAcquire(int):獨佔方式。嘗試獲取資源,成功則返回true,失敗則返回false。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tryRelease(int):獨佔方式。嘗試釋放資源,成功則返回true,失敗則返回false。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tryAcquireShared(int):共享方式。嘗試獲取資源。負數表示失敗;0表示成功,但沒有剩餘可用資源;正數表示成功,且有剩餘資源。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放後允許喚醒後續等待結點返回true,否則返回false。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"isHeldExclusively():該線程是否正在獨佔資源。只有用到condition才需要去實現它。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AQS需要子類複寫的方法均沒有聲明爲abstract,目的是避免子類需要強制性覆寫多個方法,因爲一般自定義同步器要麼是獨佔方法,要麼是共享方法,只需實現tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一種即可。當然,AQS也支持子類同時實現獨佔和共享兩種模式,如ReentrantReadWriteLock。另外可以看到,一般try開頭的都是需要鎖實現的,但是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tryAcquireNanos","attrs":{}}],"attrs":{}},{"type":"text","text":"方法例外,它的作用使用實現可超時中斷的鎖","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"源碼分析","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"獨佔鎖的實現","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"/**\n * Acquires in exclusive mode, ignoring interrupts. Implemented\n * by invoking at least once {@link #tryAcquire},\n * returning on success. Otherwise the thread is queued, possibly\n * repeatedly blocking and unblocking, invoking {@link\n * #tryAcquire} until success. This method can be used\n * to implement method {@link Lock#lock}.\n *\n * @param arg the acquire argument. This value is conveyed to\n * {@link #tryAcquire} but is otherwise uninterpreted and\n * can represent anything you like.\n */\npublic final void acquire(int arg) {\n if (!tryAcquire(arg) &&\n acquireQueued(addWaiter(Node.EXCLUSIVE), arg))\n selfInterrupt();\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述邏輯主要包括:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"嘗試獲取(調用tryAcquire更改狀態,需要保證原子性);在tryAcquire方法中使用了同步器提供的對state操作的方法,利用compareAndSet保證只有一個線程能夠對狀態進行成功修改,而沒有成功修改的線程將進入sync隊列排隊。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果獲取不到,將當前線程構造成節點Node並加入sync隊列;進入隊列的每個線程都是一個節點Node,從而形成了一個雙向隊列,類似CLH隊列,這樣做的目的是線程間的通信會被限制在較小規模(也就是兩個節點左右)。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"再次嘗試獲取(acquireQueued),如果沒有獲取到那麼將當前線程從線程調度器上摘下,進入等待狀態。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tryAcquire在ReentrantLock中的最終是在FairSync中","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"protected final boolean tryAcquire(int acquires) {\n final Thread current = Thread.currentThread();\n int c = getState();\n if (c == 0) {\n if (!hasQueuedPredecessors() &&\n compareAndSetState(0, acquires)) {\n setExclusiveOwnerThread(current);\n return true;\n }\n }\n else if (current == getExclusiveOwnerThread()) {\n int nextc = c + acquires;\n if (nextc < 0)\n throw new Error(\"Maximum lock count exceeded\");\n setState(nextc);\n return true;\n }\n return false;\n}\n//對於非公平鎖也一樣\nprotected final boolean tryAcquire(int acquires) {\n return nonfairTryAcquire(acquires);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"addWaiter","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"private Node addWaiter(Node mode) {\n Node node = new Node(Thread.currentThread(), mode);\n // Try the fast path of enq; backup to full enq on failure\n Node pred = tail;\n if (pred != null) {\n node.prev = pred;\n if (compareAndSetTail(pred, node)) {\n pred.next = node;\n return node;\n }\n }\n enq(node);\n return node;\n}\nprivate Node enq(final Node node) {\n for (;;) {\n Node t = tail;\n if (t == null) { // Must initialize\n if (compareAndSetHead(new Node()))\n tail = head;\n } else {\n node.prev = t;\n if (compareAndSetTail(t, node)) {\n t.next = node;\n return t;\n }\n }\n }\n}\nprivate final boolean compareAndSetHead(Node update) {\n return unsafe.compareAndSwapObject(this, headOffset, null, update);\n}\nprivate final boolean compareAndSetTail(Node expect, Node update) {\n return unsafe.compareAndSwapObject(this, tailOffset, expect, update);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述邏輯主要包括:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"使用當前線程構造Node;對於一個節點需要做的是將當節點前驅節點指向尾節點(current.prev = tail),尾節點指向它(tail = current),原有的尾節點的後繼節點指向它(t.next = current)。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"先行嘗試在隊尾添加;如果尾節點已經有了,然後做如下操作:(1)分配引用T指向尾節點;(2)將節點的前驅節點更新爲尾節點(current.prev = tail);(3)如果尾節點是T,那麼將當尾節點設置爲該節點(tail = current,原子更新);(4)T的後繼節點指向當前節點(T.next = current)。注意第3點是要求原子的。這樣可以以最短路徑O(1)的效果來完成線程入隊,是最大化減少開銷的一種方式。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果隊尾添加失敗或者是第一個入隊的節點。如果是第1個節點,也就是sync隊列沒有初始化,那麼會進入到enq這個方法,進入的線程可能有多個,或者說在addWaiter中沒有成功入隊的線程都將進入enq這個方法。可以看到enq的邏輯是確保進入的Node都會有機會順序的添加到sync隊列中,而加入的步驟如下:(1)如果尾節點爲空,那麼原子化的分配一個頭節點,並將尾節點指向頭節點,這一步是初始化;(2)然後是重複在addWaiter中做的工作,但是在一個while(true)的循環中,直到當前節點入隊爲止。進入sync隊列之後,接下來就是要進行鎖的獲取,或者說是訪問控制了,只有一個線程能夠在同一時刻繼續的運行,而其他的進入等待狀態。而每個線程都是一個獨立的個體,它們自省的觀察,當條件滿足的時候(自己的前驅是頭結點並且原子性的獲取了狀態),那麼這個線程能夠繼續運行。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"final boolean acquireQueued(final Node node, int arg) {\n boolean failed = true;\n try {\n boolean interrupted = false;\n for (;;) {\n final Node p = node.predecessor();\n if (p == head && tryAcquire(arg)) {\n setHead(node);\n p.next = null; // help GC\n failed = false;\n return interrupted;\n }\n if (shouldParkAfterFailedAcquire(p, node) &&\n parkAndCheckInterrupt())\n interrupted = true;\n }\n } finally {\n if (failed)\n cancelAcquire(node);\n }\n}\n\nprivate static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {\n int ws = pred.waitStatus;\n if (ws == Node.SIGNAL)\n /*\n * This node has already set status asking a release\n * to signal it, so it can safely park.\n */\n return true;\n if (ws > 0) {\n /*\n * Predecessor was cancelled. Skip over predecessors and\n * indicate retry.\n */\n do {\n node.prev = pred = pred.prev;\n } while (pred.waitStatus > 0);\n pred.next = node;\n } else {\n /*\n * waitStatus must be 0 or PROPAGATE. Indicate that we\n * need a signal, but don't park yet. Caller will need to\n * retry to make sure it cannot acquire before parking.\n */\n compareAndSetWaitStatus(pred, ws, Node.SIGNAL);\n }\n return false;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述邏輯主要包括:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"獲取當前節點的前驅節點;需要獲取當前節點的前驅節點,而頭結點所對應的含義是當前站有鎖且正在運行。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"當前驅節點是頭結點並且能夠獲取狀態,代表該當前節點佔有鎖;如果滿足上述條件,那麼代表能夠佔有鎖,根據節點對鎖佔有的含義,設置頭結點爲當前節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"否則進入等待狀態。如果沒有輪到當前節點運行,那麼將當前線程從線程調度器上摘下,也就是進入等待狀態。","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"小總結","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"狀態的維護;需要在鎖定時,需要維護一個狀態(int類型),而對狀態的操作是原子和非阻塞的,通過同步器提供的對狀態訪問的方法對狀態進行操縱,並且利用compareAndSet來確保原子性的修改。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"狀態的獲取;一旦成功的修改了狀態,當前線程或者說節點,就被設置爲頭節點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"sync隊列的維護。在獲取資源未果的過程中條件不符合的情況下(不該自己,前驅節點不是頭節點或者沒有獲取到資源)進入睡眠狀態,停止線程調度器對當前節點線程的調度。這時引入的一個釋放的問題,也就是說使睡眠中的Node或者說線程獲得通知的關鍵,就是前驅節點的通知,而這一個過程就是釋放,釋放會通知它的後繼節點從睡眠中返回準備運行。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/91/9122d52cb91ffecf0330209db366853f.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鎖的釋放","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"public final boolean release(int arg) {\n if (tryRelease(arg)) {\n Node h = head;\n if (h != null && h.waitStatus != 0)\n unparkSuccessor(h);\n return true;\n }\n return false;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述邏輯主要包括:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"嘗試釋放狀態;tryRelease能夠保證原子化的將狀態設置回去,當然需要使用compareAndSet來保證。如果釋放狀態成功過之後,將會進入後繼節點的喚醒過程。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"喚醒當前節點的後繼節點所包含的線程。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tryRelease在ReentranLock-Sync中的實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"protected final boolean tryRelease(int releases) {\n int c = getState() - releases;\n if (Thread.currentThread() != getExclusiveOwnerThread())\n throw new IllegalMonitorStateException();\n boolean free = false;\n if (c == 0) {\n free = true;\n setExclusiveOwnerThread(null);\n }\n setState(c);\n return free;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過LockSupport的unpark方法將休眠中的線程喚醒,讓其繼續acquire狀態。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"private void unparkSuccessor(Node node) {\n // 將狀態設置爲同步狀態\n int ws = node.waitStatus;\n if (ws < 0)\n compareAndSetWaitStatus(node, ws, 0);\n\n // 獲取當前節點的後繼節點,如果滿足狀態,那麼進行喚醒操作 \n // 如果沒有滿足狀態,從尾部開始找尋符合要求的節點並將其喚醒 \n Node s = node.next;\n if (s == null || s.waitStatus > 0) {\n s = null;\n for (Node t = tail; t != null && t != node; t = t.prev)\n if (t.waitStatus <= 0)\n s = t;\n }\n if (s != null)\n LockSupport.unpark(s.thread);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述邏輯主要包括,該方法取出了當前節點的next引用,然後對其線程(Node)進行了喚醒,這時就只有一個或合理個數的線程被喚醒,被喚醒的線程繼續進行對資源的獲取與爭奪。回顧整個資源的獲取和釋放過程:在獲取時,維護了一個sync隊列,每個節點都是一個線程在進行自旋,而依據就是自己是否是首節點的後繼並且能夠獲取資源;在釋放時,僅僅需要將資源還回去,然後通知一下後繼節點並將其喚醒。這裏需要注意,隊列的維護(首節點的更換)是依靠消費者(獲取時)來完成的,也就是說","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在滿足了自旋退出的條件時的一刻,這個節點就會被設置成爲首節點","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"共享鎖","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單來講,讀鎖和讀鎖是可以共享的,其它情況,讀鎖和寫鎖,寫鎖和讀鎖、寫鎖和寫鎖,都是互斥的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"/**\n * Acquires in shared mode, ignoring interrupts. Implemented by\n * first invoking at least once {@link #tryAcquireShared},\n * returning on success. Otherwise the thread is queued, possibly\n * repeatedly blocking and unblocking, invoking {@link\n * #tryAcquireShared} until success.\n *\n * @param arg the acquire argument. This value is conveyed to\n * {@link #tryAcquireShared} but is otherwise uninterpreted\n * and can represent anything you like.\n */\npublic final void acquireShared(int arg) {\n if (tryAcquireShared(arg) < 0)\n doAcquireShared(arg);\n}\n\nprivate void doAcquireShared(int arg) {\n final Node node = addWaiter(Node.SHARED);\n boolean failed = true;\n try {\n boolean interrupted = false;\n for (;;) {\n final Node p = node.predecessor();\n if (p == head) {\n int r = tryAcquireShared(arg);\n if (r >= 0) {\n setHeadAndPropagate(node, r);\n p.next = null; // help GC\n if (interrupted)\n selfInterrupt();\n failed = false;\n return;\n }\n }\n if (shouldParkAfterFailedAcquire(p, node) &&\n parkAndCheckInterrupt())\n interrupted = true;\n }\n } finally {\n if (failed)\n cancelAcquire(node);\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述邏輯主要包括:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"嘗試獲取共享狀態;調用tryAcquireShared來獲取共享狀態,該方法是非阻塞的,如果獲取成功則立刻返回,也就表示獲取共享鎖成功。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"獲取失敗進入sync隊列;在獲取共享狀態失敗後,當前時刻有可能是獨佔鎖被其他線程所把持,那麼將當前線程構造成爲節點(共享模式)加入到sync隊列中。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"循環內判斷退出隊列條件;如果當前節點的前驅節點是頭結點並且獲取共享狀態成功,這裏和獨佔鎖acquire的退出隊列條件類似。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"獲取共享狀態成功;在退出隊列的條件上,和獨佔鎖之間的主要區別在於獲取共享狀態成功之後的行爲,而如果共享狀態獲取成功之後會判斷後繼節點是否是共享模式,如果是共享模式,那麼就直接對其進行喚醒操作,也就是同時激發多個線程併發的運行。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"獲取共享狀態失敗。通過使用LockSupport將當前線程從線程調度器上摘下,進入休眠狀態。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"/**\n * Releases in shared mode. Implemented by unblocking one or more\n * threads if {@link #tryReleaseShared} returns true.\n *\n * @param arg the release argument. This value is conveyed to\n * {@link #tryReleaseShared} but is otherwise uninterpreted\n * and can represent anything you like.\n * @return the value returned from {@link #tryReleaseShared}\n */\npublic final boolean releaseShared(int arg) {\n if (tryReleaseShared(arg)) {\n doReleaseShared();\n return true;\n }\n return false;\n}\nprivate void doReleaseShared() {\n /*\n * Ensure that a release propagates, even if there are other\n * in-progress acquires/releases. This proceeds in the usual\n * way of trying to unparkSuccessor of head if it needs\n * signal. But if it does not, status is set to PROPAGATE to\n * ensure that upon release, propagation continues.\n * Additionally, we must loop in case a new node is added\n * while we are doing this. Also, unlike other uses of\n * unparkSuccessor, we need to know if CAS to reset status\n * fails, if so rechecking.\n */\n for (;;) {\n Node h = head;\n if (h != null && h != tail) {\n int ws = h.waitStatus;\n if (ws == Node.SIGNAL) {\n if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))\n continue; // loop to recheck cases\n unparkSuccessor(h);\n }\n else if (ws == 0 &&\n !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))\n continue; // loop on failed CAS\n }\n if (h == head) // loop if head changed\n break;\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"調用該方法釋放共享狀態,每次獲取共享狀態acquireShared都會操作狀態,同樣在共享鎖釋放的時候,也需要將狀態釋放。比如說,一個限定一定數量訪問的同步工具,每次獲取都是共享的,但是如果超過了一定的數量,將會阻塞後續的獲取操作,只有當之前獲取的消費者將狀態釋放纔可以使阻塞的獲取操作得以運行。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"未完待續","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可中斷(public final void acquireInterruptibly(int arg))","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"超時控制 (private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"條件中斷","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"參考","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"http://ifeve.com/introduce-abstractqueuedsynchronizer/","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章