编程知识 cdmana.com

Reentrantlock of JDK source code series

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" Recently will "},{"type":"text","marks":[{"type":"strong"}],"text":"ReentrantLock"},{"type":"text","text":" I learned it once and read the source code again , Record the learning process "}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JDK Source series "}]},{"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 Source code series 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 Source code series HashMap"}]}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":" Use "}]},{"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":" Using the lock mechanism , To ensure thread safety "}]},{"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 // Resource blocks protected by this lock \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":" Or you can use tryLock() Method , In multithreading , When a thread releases a lock , Just try to get the lock ."}]},{"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 // Resource blocks protected by this lock \n } finally {\n lock.unlock();\n }\n } else {\n // Other operations , Not protected by a lock \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":" This method , Unlock until you make sure you get the lock , And it doesn't try to unlock when it's not locked ."}]},{"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":" If you try to get a lock, it's too long , You can also add time to the process of acquiring a lock , Timeout will directly interrupt the thread ."}]},{"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":" If you want the module of the current lock not to execute immediately , You can also call await Mechanism "}]},{"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":" occasionally , When you encounter a long business process , It's been too long to hold , Consider the mechanism of breaking the lock , Release the lock ."}]},{"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":" Application scenario comparison "}]},{"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":" Source code "}]},{"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":" First look at the class "}]},{"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":" Inherit upward "},{"type":"text","marks":[{"type":"strong"}],"text":"Lock"},{"type":"text","text":" Interface , as well as "},{"type":"text","marks":[{"type":"strong"}],"text":"Serializable"},{"type":"text","text":", It's all implemented "},{"type":"text","marks":[{"type":"strong"}],"text":"Lock"},{"type":"text","text":" Methods ."}]},{"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() Get the lock "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"void lockInterruptibly() Interrupt locking mechanism "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"boolean tryLock() Other threads have release locks , To get the lock "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"boolean tryLock(long time, TimeUnit unit) Thread is idle time for a given time , And other threads have release locks , To get the lock "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"void unlock() Release the lock "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Condition newCondition() Give the lock binding condition "}]}]}]},{"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":" Instantiate lock "}]},{"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":" here , There are two structures , One is a nonparametric construction , One is to pass in a Boolean value ."}]},{"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 // Create an unfair lock \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 Fair lock false Not fair lock \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":" By default, we construct an unfair lock , You can also directly set what type of lock to create , Like fair lock 、 Not fair lock ."}]},{"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":" Let's look at the fair lock first ."}]},{"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 // When using lock.lock() When it comes to At the same time the incoming 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 // here acquires = 1 refer to Locked state 0 It's not locked \n protected final boolean tryAcquire(int acquires) {\n // Get the current thread object \n final Thread current = Thread.currentThread();\n // Whether it is locked or not , The first default is 0\n int c = getState();\n if (c == 0) {\n // hasQueuedPredecessors Is it the current thread , The data structure of the two-way linked list is used , Make one queue, This means that the thread can be locked at this time . There's no need to line up \n if (!hasQueuedPredecessors() &&\n // CAS After comparison, the modification is successful \n compareAndSetState(0, acquires)) {\n // Set the current thread to exclusive thread \n setExclusiveOwnerThread(current);\n // Lock acquired successfully \n return true;\n }\n }\n // Exclusive thread \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 // The state of the synchronization lock = 1\n setState(nextc);\n return true;\n }\n // Other threads did not get lock \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 // Try to lock \n if (!tryAcquire(arg) &&\n // First add the current thread of the thread to the queue, the last one , And then I was deciding if he was locked \n acquireQueued(addWaiter(Node.EXCLUSIVE), arg))\n // If not, interrupt the thread directly \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 // Threads in the queue holding the lock \n final boolean acquireQueued(final Node node, int arg) {\n boolean failed = true;\n try {\n boolean interrupted = false;\n // It's a dead loop where you determine who is currently getting the lock \n for (;;) {\n // Point to next node \n final Node p = node.predecessor();\n // The first one in the queue , And get the lock , Turn the current thread into an exclusive thread \n if (p == head && tryAcquire(arg)) {\n setHead(node);\n p.next = null; // help GC\n failed = false;\n // If you get the lock , You do not need to interrupt the current thread and return a false\n return interrupted;\n }\n // The thread is blocked \n if (shouldParkAfterFailedAcquire(p, node) &&\n parkAndCheckInterrupt())\n // Interrupt the mechanism of lock acquisition \n interrupted = true;\n }\n } finally {\n // If you get the lock Then, no lock operation is needed 、 If not, cancel \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":" Fair lock , The current thread goes to the last one in a queue , Waiting for him to lock . If the current thread indicates that the lock is obtained , And it's also the first location for the queue . Will turn itself into an exclusive thread of a lock , Only one holds the object of this lock . At the same time CAS The exchange of atoms , Set state to 1. among 0 by It's not locked ,1 It's locked ."}]},{"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":" Not fair lock "}]},{"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 // First line preemptive lock , If you can modify the status , It will be locked directly \n if (compareAndSetState(0, 1))\n // Set exclusive thread \n setExclusiveOwnerThread(Thread.currentThread());\n else\n // Lock \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 // This is almost the same as the fair lock \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":" Not fair lock , First line preemptive lock , First, check if the thread has released the lock , If you release , The current thread is locked directly , The advantage is to reduce the waiting between threads , Speed up the locking mechanism , The delay caused by competition is avoided , Improved performance , If the lock is not released , Then enter the last queue to wait for the lock ."}]},{"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":" To sum up ."}]},{"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":" Release lock mechanism ."}]},{"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 // carry 1\n sync.release(1);\n }\n\n\n public final boolean release(int arg) {\n // Successful release means true It is false\n if (tryRelease(arg)) {\n Node h = head;\n if (h != null && h.waitStatus != 0)\n // Wake up the thread behind the queue \n unparkSuccessor(h);\n // Release successful \n return true;\n }\n // Release failed \n return false;\n }\n\n\n\n protected final boolean tryRelease(int releases) {\n\n // c = 0\n int c = getState() - releases;\n // If it is not the current thread and not the exclusive thread \n if (Thread.currentThread() != getExclusiveOwnerThread())\n // Throw it wrong \n throw new IllegalMonitorStateException();\n boolean free = false;\n if (c == 0) {\n // Release the lock \n free = true;\n // Exclusive thread settings 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":" The release of the lock , Relatively simple . Reset the lock state back to 0, At the same time, the exclusive thread is also set null, Then wake up the threads in the queue , Complete release ."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":" Source code summary "}]},{"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":" At the time of creation , Default is unfair lock , But you can also be in the construction time , You can also create a fair lock . Among them through "},{"type":"text","marks":[{"type":"strong"}],"text":"CAS"},{"type":"text","text":" change "},{"type":"text","marks":[{"type":"strong"}],"text":"state"},{"type":"text","text":" State to change the lock value , 0 Indicates that there is a lock that can be obtained ,1 Indicates that the lock has been acquired , To set the exclusive thread of the lock ."}]},{"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":" In the fair lock mechanism , The thread that requests the lock is queued directly into a queue ( A queue simulated by a bidirectional linked list ) The last , To get the lock ."}]},{"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":" In the mechanism of unfair lock , The thread requesting the lock will first pass through "},{"type":"text","marks":[{"type":"strong"}],"text":"CAS"},{"type":"text","text":" To change "},{"type":"text","marks":[{"type":"strong"}],"text":"state"},{"type":"text","text":" Lock state of , If you can change (0 -> 1), The lock is obtained directly , Set itself to an exclusive lock . This reduces the number of queues 、 Load queue 、 Wake up threads and other performance consumption . If it cannot be modified to "},{"type":"text","marks":[{"type":"strong"}],"text":"state"},{"type":"text","text":" The state of , It will also become a mechanism for fair lock , Enter the last of the queue , Wait for it to get the lock ."}]},{"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":" The release of the lock , take "},{"type":"text","marks":[{"type":"strong"}],"text":"state"},{"type":"text","text":" Reset back to 0, At the same time monopolize the thread ( You can also think of this as a thread object holding a lock ) Set up null, Wake up the thread that is next to it . This series of steps is done , The lock is released ."}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":" Advantages and disadvantages "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":" Not fair lock "},{"type":"text","text":", It's true that the performance is relatively high . But there is also an obvious disadvantage , We can imagine that , When you're in line for dinner , It's your turn to eat , At this moment, a man comes in front of you , I had a meal in advance , It has led to your rice growing , If at this time, a few people suddenly cut in front of you to make a meal , It will continue to cause you to cook longer . If you put it in the thread , Suddenly other threads get the lock in advance , That will cause the current thread to acquire the lock for a longer time , And cause the thread to block , It's too late to get the lock ."}]},{"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":" So we have to choose the appropriate lock type according to the business , To lock , Try to avoid blocking some important business due to lock ."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":" Statement "}]},{"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":" author : Sinsy Link to this article :https://blog.sincehub.cn/2020/11/10/jdk-reentrantLock/ "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" Copyright notice : This article is an original blog article , follow "},{"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":" Copyright agreement , Please attach the original statement for reprint . If you have any business cooperation or authorization negotiation , Please leave me a message :550569627@qq.com"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":" quote "}]},{"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}}]}

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢

Scroll to Top