jdk 源碼系列之ReentrantLock

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近將 "},{"type":"text","marks":[{"type":"strong"}],"text":"ReentrantLock"},{"type":"text","text":" 學了一遍同時也把源碼讀了一遍,記錄下學習的過程 "}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JDK 源碼系列"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/09/29/jdk-StringBuilder-StringBuffer/","title":null},"content":[{"type":"text","text":"jdk 源碼系列之StringBuilder、StringBuffer"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://blog.sincehub.cn/2020/10/31/jdk-HashMap/","title":null},"content":[{"type":"text","text":"jdk 源碼系列之HashMap"}]}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"使用"}]},{"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":"使用鎖機制,來保障線程安全"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Lock lock = new ReentrantLock();\n\n lock.lock();\n\n try {\n // 受此鎖保護的資源塊\n } finally {\n lock.unlock();\n }"}]},{"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":"或者你可以使用 tryLock() 方法,在多線程中,當一個線程釋放鎖的時候,就嘗試去獲取鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Lock lock = new ReentrantLock();\n if (lock.tryLock()) {\n try {\n // 受此鎖保護的資源塊\n } finally {\n lock.unlock();\n }\n } else {\n // 進行其他操作,未被鎖保護\n }"}]},{"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":"這種方法,在確保獲取鎖的時候纔會解鎖,並且在未獲鎖時不會嘗試去解鎖。"}]},{"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":"如果嘗試去獲取鎖的時候太長,也可以給獲取鎖的這個過程加上時間,超時則直接中斷線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static void main(String[] args) throws InterruptedException {\n Lock lock = new ReentrantLock();\n if (lock.tryLock(5, TimeUnit.SECONDS)) {\n try {\n // manipulate protected state\n\n } \n finally {\n lock.unlock();\n }\n } else {\n // perform alternative actions\n }\n\n }\n\n}"}]},{"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":"如果希望當前鎖的模塊不是立刻執行,也可以調用 await 機制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"Lock lock = new ReentrantLock();\n lock.lock();\n try {\n // manipulate protected state\n lock.newCondition().await(5, TimeUnit.SECONDS);\n }\n finally {\n lock.unlock();\n }"}]},{"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":"有時候,當遇到過長的業務流程,導致持有的時間太長了,可以考慮打斷鎖的機制,釋放鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Lock lock = new ReentrantLock();\n lock.lock();\n try {\n // manipulate protected state\n long startTime = System.currentTimeMillis();\n long endTime = System.currentTimeMillis();\n if (endTime - startTime > 10) {\n lock.lockInterruptibly();\n try {\n } finally {\n lock.unlock();\n }\n }\n\n lock.newCondition().await(5, TimeUnit.SECONDS);\n }\n finally {\n lock.unlock();\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"應用場景比較"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bb/bbca72cf9fb58fb4373554a908369d91.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"源碼"}]},{"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":"先看類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ReentrantLock implements Lock, java.io.Serializable {\n private static final long serialVersionUID = 7373984872572414699L;"}]},{"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":"向上繼承了 "},{"type":"text","marks":[{"type":"strong"}],"text":"Lock"},{"type":"text","text":" 接口,以及 "},{"type":"text","marks":[{"type":"strong"}],"text":"Serializable"},{"type":"text","text":",都是實現了 "},{"type":"text","marks":[{"type":"strong"}],"text":"Lock"},{"type":"text","text":" 的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"void lock() 獲取鎖"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"void lockInterruptibly() 中斷鎖機制"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"boolean tryLock() 其他線程有釋放鎖,才調用獲取鎖"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"boolean tryLock(long time, TimeUnit unit) 給定時間內線程是空閒時間,且其他線程有釋放鎖,才調用獲取鎖"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"void unlock() 釋放鎖"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Condition newCondition() 給鎖綁定條件"}]}]}]},{"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":"實例化鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Lock lock = new ReentrantLock();\n\nLock lock1 = new ReentrantLock(false);\n\nLock lock2 = new ReentrantLock(true);"}]},{"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":"這裏,有兩個構造,一個是無參構造,一個是傳入布爾值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * Creates an instance of {@code ReentrantLock}.\n * This is equivalent to using {@code ReentrantLock(false)}.\n */\n public ReentrantLock() {\n // 創建一個非公平鎖\n sync = new NonfairSync();\n }\n\n /**\n * Creates an instance of {@code ReentrantLock} with the\n * given fairness policy.\n *\n * @param fair {@code true} if this lock should use a fair ordering policy\n */\n public ReentrantLock(boolean fair) {\n // true 公平鎖 false 非公平鎖\n sync = fair ? new FairSync() : new NonfairSync();\n }"}]},{"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":"這裏默認是構造一個非公平鎖,也可以直接設置創建什麼類型鎖,比如公平鎖、非公平鎖。"}]},{"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":"我們先看看公平鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * Sync object for fair locks\n */\n static final class FairSync extends Sync {\n private static final long serialVersionUID = -3000897897090466540L;\n\n final void lock() {\n // 當使用 lock.lock() 的時候調到 同時傳入1\n acquire(1);\n }\n\n /**\n * Fair version of tryAcquire. Don't grant access unless\n * recursive call or no waiters or is first.\n */\n // 這裏 acquires = 1指的是 上鎖的狀態 0則是未上鎖\n protected final boolean tryAcquire(int acquires) {\n // 獲取當前線程對象\n final Thread current = Thread.currentThread();\n // 是否上鎖,首次默認是0\n int c = getState();\n if (c == 0) {\n // hasQueuedPredecessors 是否是當前線程,使用了雙向鏈表的數據結構,做一個 queue,這裏的意思大概是到了這個線程可以加鎖了。不用排隊了\n if (!hasQueuedPredecessors() &&\n // CAS 比較後修改成功\n compareAndSetState(0, acquires)) {\n // 將當前線程設置成獨佔線程\n setExclusiveOwnerThread(current);\n // 獲取鎖成功\n return true;\n }\n }\n // 獨佔線程\n else if (current == getExclusiveOwnerThread()) {\n // nextc = 1\n int nextc = c + acquires;\n if (nextc < 0)\n throw new Error(\"Maximum lock count exceeded\");\n // 同步鎖的狀態 = 1\n setState(nextc);\n return true;\n }\n // 其他線程則未獲取到鎖\n return false;\n }\n }\n\n /**\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 */\n public final void acquire(int arg) {\n // 嘗試上鎖\n if (!tryAcquire(arg) &&\n // 先將線程當前線程添加進隊列最後一個,然後在判斷是否到他上鎖了\n acquireQueued(addWaiter(Node.EXCLUSIVE), arg))\n // 沒有則直接打斷線程\n selfInterrupt();\n }\n\n\n /**\n * Acquires in exclusive uninterruptible mode for thread already in\n * queue. Used by condition wait methods as well as acquire.\n *\n * @param node the node\n * @param arg the acquire argument\n * @return {@code true} if interrupted while waiting\n */\n // 持有鎖的隊列裏面的線程\n final boolean acquireQueued(final Node node, int arg) {\n boolean failed = true;\n try {\n boolean interrupted = false;\n // 一個死循環你判斷裏面當前誰獲得鎖\n for (;;) {\n // 指向下一個節點\n final Node p = node.predecessor();\n // 隊列的第一個,且獲取鎖,將當前線程變成獨佔線程\n if (p == head && tryAcquire(arg)) {\n setHead(node);\n p.next = null; // help GC\n failed = false;\n // 如果獲得到鎖,則不需要打斷當前線程返回一個 false\n return interrupted;\n }\n // 線程阻塞了\n if (shouldParkAfterFailedAcquire(p, node) &&\n parkAndCheckInterrupt())\n // 中斷獲取鎖的機制\n interrupted = true;\n }\n } finally {\n // 如果獲得到鎖 則不需要取消加鎖操作、反之則取消\n if (failed)\n cancelAcquire(node);\n }\n }"}]},{"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":"公平鎖,當前的線程會進入一個隊列中最後一個,等待到他的上鎖。如果當前的線程表示獲取到鎖,且也是隊列的第一個位置。會將自己變成鎖的獨佔線程,只有自己才持有這個鎖的對象。同時這裏進行了 CAS 的原子交換,設置狀態爲1。其中 0 爲 未上鎖狀態,1爲上鎖狀態。"}]},{"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":"非公平鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * Sync object for non-fair locks\n */\n static final class NonfairSync extends Sync {\n private static final long serialVersionUID = 7316153563782823691L;\n\n /**\n * Performs lock. Try immediate barge, backing up to normal\n * acquire on failure.\n */\n final void lock() {\n // 先進行搶佔鎖,如果如可以修改狀態,則直接變成上鎖狀態\n if (compareAndSetState(0, 1))\n // 設置獨佔線程\n setExclusiveOwnerThread(Thread.currentThread());\n else\n // 加鎖\n acquire(1);\n }\n\n protected final boolean tryAcquire(int acquires) {\n return nonfairTryAcquire(acquires);\n }\n }\n\n /**\n * Performs non-fair tryLock. tryAcquire is implemented in\n * subclasses, but both need nonfair try for trylock method.\n */\n final boolean nonfairTryAcquire(int acquires) {\n // 這個和公平鎖裏面差不多\n final Thread current = Thread.currentThread();\n int c = getState();\n if (c == 0) {\n if (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) // overflow\n throw new Error(\"Maximum lock count exceeded\");\n setState(nextc);\n return true;\n }\n return false;\n }"}]},{"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":"非公平鎖,先進行搶佔鎖,先查看線程是否釋放了鎖,如果釋放了,當前線程則直接上鎖,這樣的好處就是減少了線程之間的等待,加快了上鎖的機制,避免了排隊時競爭所導致的延時,提高了性能,如果沒有釋放鎖,則進入到隊列的最後一個等待上鎖。"}]},{"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":"小結一下。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d9/d916c495b072428a5c2c18019f654e32.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"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":"釋放鎖機制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\t\t/**\n * Attempts to release this lock.\n *\n *

If the current thread is the holder of this lock then the hold\n * count is decremented. If the hold count is now zero then the lock\n * is released. If the current thread is not the holder of this\n * lock then {@link IllegalMonitorStateException} is thrown.\n *\n * @throws IllegalMonitorStateException if the current thread does not\n * hold this lock\n */\n public void unlock() {\n // 攜帶 1\n sync.release(1);\n }\n\n\n public final boolean release(int arg) {\n // 釋放成功則爲 true 否則是 false\n if (tryRelease(arg)) {\n Node h = head;\n if (h != null && h.waitStatus != 0)\n // 喚醒隊列後面的線程\n unparkSuccessor(h);\n // 釋放成功\n return true;\n }\n // 釋放失敗\n return false;\n }\n\n\n\n protected final boolean tryRelease(int releases) {\n\n // c = 0\n int c = getState() - releases;\n // 如果不是當前線程和不是獨佔線程\n if (Thread.currentThread() != getExclusiveOwnerThread())\n // 直接拋出錯\n throw new IllegalMonitorStateException();\n boolean free = false;\n if (c == 0) {\n // 釋放鎖\n free = true;\n // 獨佔線程設置null\n setExclusiveOwnerThread(null);\n }\n // 0\n setState(c);\n return free;\n }"}]},{"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":"鎖的釋放,比較簡單。將鎖狀態重新設置回 0,同時獨佔線程也設置null,之後喚醒後面的隊列裏面的線程,完成釋放。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"源碼總結"}]},{"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","marks":[{"type":"strong"}],"text":"ReentrantLock"},{"type":"text","text":" 創建的時候,默認是非公平鎖,不過你也可以在構造的時候,也可以創建一個公平鎖。 其中通過 "},{"type":"text","marks":[{"type":"strong"}],"text":"CAS"},{"type":"text","text":" 改變 "},{"type":"text","marks":[{"type":"strong"}],"text":"state"},{"type":"text","text":" 的狀態來改變鎖的數值, 0 表示有鎖可以獲取,1 表示鎖已被獲取,來設置鎖的獨佔線程。"}]},{"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":"在公平鎖的機制中,請求鎖的線程會直接排到一個隊列中(通過一個雙向鏈表來模擬的隊列)的最後一個,去獲取鎖。"}]},{"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":"非公平鎖的機制中,請求鎖的線程首先會先通過 "},{"type":"text","marks":[{"type":"strong"}],"text":"CAS"},{"type":"text","text":" 來改變 "},{"type":"text","marks":[{"type":"strong"}],"text":"state"},{"type":"text","text":" 的鎖狀態,如果可以改變(0 -> 1),則直接獲取到鎖,將自身設置成獨佔鎖。這樣的好處就減少了一些進隊列、加載隊列、喚醒線程等性能消耗。如果未能修改到 "},{"type":"text","marks":[{"type":"strong"}],"text":"state"},{"type":"text","text":" 的狀態,也會變成公平鎖的機制,進入到隊列的最後一個,等待到它去獲取鎖。"}]},{"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":"鎖的釋放,將 "},{"type":"text","marks":[{"type":"strong"}],"text":"state"},{"type":"text","text":" 重新設置回 0,同時獨佔線程(你也可以認爲這是持有鎖的線程對象)設置null,之後喚醒排在它下個的線程。這一系列步驟做完,則宣告鎖的釋放。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"優缺點 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"非公平鎖"},{"type":"text","text":",確實性能比較高。不過也有一個顯而易見的缺點,我們可以想象一下,當你在排隊喫飯的時候,輪到你喫飯的時候,這時候突然來一個人插在你前面,提前打飯了,導致你打飯時間變長了,如果這時候在有幾個人在也突然插到你前打飯,又會繼續導致你打飯時間變得更長。那如果放到線程裏面,突然其他線程提前獲取到了鎖,那會導致當前線程獲取到鎖時間變長,而導致線程阻塞,遲遲未獲取到鎖。"}]},{"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":"所以得根據業務去選擇合適的鎖類型,進行上鎖,儘可能的避免有一些重要的業務因爲上鎖而阻塞到。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"聲明"}]},{"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":"作者: Sinsy 本文鏈接:https://blog.sincehub.cn/2020/11/10/jdk-reentrantLock/ "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"版權聲明:本文爲博主原創文章,遵循 "},{"type":"link","attrs":{"href":"https://creativecommons.org/licenses/by-sa/4.0/deed.zh","title":null},"content":[{"type":"text","text":"CC 4.0 BY-SA"}]},{"type":"text","text":" 版權協議,轉載請附上原文聲明。 如您有任何商業合作或者授權方面的協商,請給我留言:[email protected]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"引用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[1] "},{"type":"link","attrs":{"href":"https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html","title":null},"content":[{"type":"text","text":"Lock (Java Platform SE 8 )"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章