🏆【算法數據結構專題】「線程鎖算法專項」初探CLH隊列鎖機制原理分析

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"技術擴展","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"SMP(對稱多處理器架構)","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SMP(Symmetric Multi-Processor),即對稱多處理器結構,指服務器中多個CPU對稱工作,每個CPU訪問內存地址所需時間相同。其主要特徵是共享,包含對CPU,內存,I/O等進行共享","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/43/43b2418e45ecaa02cac0e7afb64e0733.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"SMP優點是能夠保證內存一致性,缺點是這些共享的資源很可能成爲性能瓶頸,隨着CPU數量的增加,每個CPU都要訪問相同的內存資源,可能導致內存訪問衝突,可能會導致CPU資源的浪費。常用的PC機就屬於這種","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"NUMA(非一致性內存訪問)","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"NUMA(Non-Uniform Memory Access)非一致存儲訪問, 將CPU分爲CPU模塊,每個CPU模塊由多個CPU組成, 並且具有獨立的本地內存、 I/O 槽口等,模塊之間可以通過互聯模塊相互訪問 ,訪問本地內存的速度將遠遠高於訪問遠地內存 ( 系統內其它節點的內存 ) 的速度,這也是非一致存儲訪問 NUMA 的由來","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/10/10788c5e838da8eb0ff34bb0ec536196.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"NUMA優點是可以較好地解決原來SMP系統的擴展問題,缺點是由於訪問遠程內存的延時遠遠超過本地內存,因此當CPU數量增加時,系統性能無法線性增加","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"自旋鎖和互斥鎖","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"CLH鎖是一種自旋鎖,那麼我們先來看看自旋鎖是什麼?","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":5},"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","marks":[{"type":"strong","attrs":{}}],"text":"自旋鎖說白了也是一種互斥鎖,只不過沒有搶到鎖的線程會一直自旋等待鎖的釋放,處於busy-waiting的狀態,此時等待鎖的線程不會進入休眠狀態,而是一直忙等待浪費CPU週期。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"因此自旋鎖適用於鎖佔用時間短的場合","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":5},"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","marks":[{"type":"strong","attrs":{}}],"text":"互斥鎖說的是傳統意義的互斥鎖,就是多個線程併發競爭鎖的時候,沒有搶到鎖的線程會進入休眠狀態即【sleep-waiting】","attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"當鎖被釋放時候,處於休眠狀態的一個線程會再次獲取到鎖","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"缺點:就是這一些列過程需要線程切換,需要執行很多CPU指令,同樣需要時間。如果CPU執行線程切換的時間比鎖佔用的時間還長,那麼可能還不如使用自旋鎖。因此互斥鎖適用於鎖佔用時間長的場合","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"CLH鎖機制","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"CLH鎖其實就是一種是基於邏輯隊列非線程飢餓的一種自旋公平鎖,由於是 Craig、Landin 和 Hagersten三位大佬的發明,因此命名爲CLH鎖,CLH是一種基於單向鏈表的高性能、能確保無飢餓性,提供先來先服務公平性的自旋鎖","attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"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","marks":[{"type":"strong","attrs":{}}],"text":"申請加鎖的線程通過前驅節點的變量進行自旋","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"前置節點解鎖後,當前節點會結束自旋,並進行加鎖","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"CLH節點模型","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"CLH隊列中的結點QNode中含有一個locked字段,該字段若爲true表示該線程需要獲取鎖,且不釋放鎖,爲false表示線程釋放了鎖。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"結點之間是通過隱形的鏈表相連,之所以叫隱形的鏈表是因爲這些結點之間沒有明顯的next指針,而是通過myPred所指向的結點的變化情況來影響myNode的行爲。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"CLH鎖原理","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"首先有一個尾節點指針,通過這個尾結點指針來構建等待線程的邏輯隊列","attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"因此能確保線程線程先到先服務的公平性,因此尾指針可以說是構建邏輯隊列的橋樑","attrs":{}},{"type":"text","text":";","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"此外這個尾節點指針是原子引用類型,避免了多線程併發操作的線程安全性問題","attrs":{}},{"type":"text","text":";","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"通過等待鎖的每個線程在自己的某個變量上自旋等待,這個變量將由前一個線程寫入。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"由於某個線程獲取鎖操作時總是通過尾節點指針獲取到前一線程寫入的變量,而尾節點指針又是原子引用類型,因此確保了這個變量獲取出來總是線程安全的","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"CLH鎖分析","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在SMP架構下,CLH更具有優勢。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在NUMA架構下,如果當前節點與前驅節點不在同一CPU模塊下,跨CPU模塊會帶來額外的系統開銷,而MCS鎖更適用於NUMA架構","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a01c017088d3c064375b322ee56bbbae.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":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","marks":[{"type":"strong","attrs":{}}],"text":"獲取當前線程的鎖節點,如果爲空,則進行初始化;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"sync方法獲取鏈表的尾節點,並將當前節點置爲尾節點,此時原來的尾節點爲當前節點的前置節點","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"如果尾節點爲空,表示當前節點是第一個節點,直接加鎖成功","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"如果尾節點不爲空,則基於前置節點的鎖值(locked==true)進行自旋,直到前置節點的鎖值變爲false","attrs":{}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"獲取當前線程對應的鎖節點,如果節點爲空或者鎖值爲false,則無需解鎖,直接返回","attrs":{}},{"type":"text","text":";","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"【sync方法爲尾節點賦空值,賦值不成功表示當前節點不是尾節點,則需要將當前節點的locked=false解鎖節點】。如果當前節點是尾節點,則無需爲該節點設置","attrs":{}},{"type":"text","text":"。","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","marks":[{"type":"strong","attrs":{}}],"text":"CLHLock上還有一個尾指針,始終指向隊列的最後一個結點。","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","marks":[{"type":"strong","attrs":{}}],"text":"CLHLock的類圖如下所示","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/42/421b472f3cea1a8ddd5e0078ec1a67a3.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/28c974eaf92a5cc95e7ea75c91c862ee.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":3},"content":[{"type":"text","text":"簡易代碼","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// CLHLock.java\n\npublic class CLHLock {\n /**\n * CLH鎖節點\n */\n private static class CLHNode {\n // 鎖狀態:默認爲false,表示線程沒有獲取到鎖;true表示線程獲取到鎖或正在等待\n // 爲了保證locked狀態是線程間可見的,因此用volatile關鍵字修飾\n volatile boolean locked = false;\n }\n // 尾結點,總是指向最後一個CLHNode節點\n // 【注意】這裏用了java的原子系列之AtomicReference,能保證原子更新\n private final AtomicReference tailNode;\n // 當前節點的前繼節點\n private final ThreadLocal predNode;\n // 當前節點\n private final ThreadLocal curNode;\n\n // CLHLock構造函數,用於新建CLH鎖節點時做一些初始化邏輯\n public CLHLock() {\n // 初始化時尾結點指向一個空的CLH節點\n tailNode = new AtomicReference<>(new CLHNode());\n // 初始化當前的CLH節點\n curNode = new ThreadLocal() {\n @Override\n protected CLHNode initialValue() {\n return new CLHNode();\n }\n };\n // 初始化前繼節點,注意此時前繼節點沒有存儲CLHNode對象,存儲的是null\n predNode = new ThreadLocal();\n }\n /**\n * 獲取鎖\n */\n public void lock() {\n // 取出當前線程ThreadLocal存儲的當前節點,初始化值總是一個新建的CLHNode,locked狀態爲false。\n CLHNode currNode = curNode.get();\n // 此時把lock狀態置爲true,表示一個有效狀態,\n // 即獲取到了鎖或正在等待鎖的狀態\n currNode.locked = true;\n // 當一個線程到來時,總是將尾結點取出來賦值給當前線程的前繼節點;\n // 然後再把當前線程的當前節點賦值給尾節點\n // 【注意】在多線程併發情況下,這裏通過AtomicReference類能防止併發問題\n // 【注意】哪個線程先執行到這裏就會先執行predNode.set(preNode);語句,因此構建了一條邏輯線程等待鏈\n // 這條鏈避免了線程飢餓現象發生\n CLHNode preNode = tailNode.getAndSet(currNode);\n // 將剛獲取的尾結點(前一線程的當前節點)付給當前線程的前繼節點ThreadLocal\n // 【思考】這句代碼也可以去掉嗎,如果去掉有影響嗎?\n predNode.set(preNode);\n // 【1】若前繼節點的locked狀態爲false,則表示獲取到了鎖,不用自旋等待;\n // 【2】若前繼節點的locked狀態爲true,則表示前一線程獲取到了鎖或者正在等待,自旋等待\n while (preNode.locked) {\n System.out.println(\"線程\" + Thread.currentThread().getName() + \"沒能獲取到鎖,進行自旋等待。。。\");\n }\n // 能執行到這裏,說明當前線程獲取到了鎖\n System.out.println(\"線程\" + Thread.currentThread().getName() + \"獲取到了鎖!!!\");\n }\n\n /**\n * 釋放鎖\n */\n public void unLock() {\n // 獲取當前線程的當前節點\n CLHNode node = curNode.get();\n // 進行解鎖操作\n // 這裏將locked至爲false,此時執行了lock方法正在自旋等待的後繼節點將會獲取到鎖\n // 【注意】而不是所有正在自旋等待的線程去併發競爭鎖\n node.locked = false;\n System.out.println(\"線程\" + Thread.currentThread().getName() + \"釋放了鎖!!!\");\n // 小夥伴們可以思考下,下面兩句代碼的作用是什麼??\n CLHNode newCurNode = new CLHNode();\n curNode.set(newCurNode);\n\n // 【優化】能提高GC效率和節省內存空間,請思考:這是爲什麼?\n // curNode.set(predNode.get());\n }\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"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","marks":[{"type":"strong","attrs":{}}],"text":"當一個線程需要獲取鎖時,會創建一個新的QNode,將其中的locked設置爲true表示需要獲取鎖","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"然後線程對tail域調用getAndSet方法,使自己成爲隊列的尾部,同時獲取一個指向其前趨的引用myPred。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"然後該線程就在前趨結點的locked字段上旋轉,直到前趨結點釋放鎖。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"當一個線程需要釋放鎖時,將當前結點的locked域設置爲false,同時回收前趨結點","attrs":{}},{"type":"text","text":"。","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","marks":[{"type":"strong","attrs":{}}],"text":"該算法也是空間有效的,如果有n個線程,L個鎖,每個線程每次只獲取一個鎖,那麼需要的存儲空間是O(L+n),n個線程有n個myNode,L個鎖有L個tail。","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","marks":[{"type":"strong","attrs":{}}],"text":"這種算法有一個缺點是在NUMA系統架構下性能表現很差,因爲它自旋的locked是前驅線程的,如果內存位置較遠,那麼性能會受到損失。【但是在SMP這種cache一致性的系統架構上表現良好。】","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","marks":[{"type":"strong","attrs":{}}],"text":"線程A需要獲取鎖,其myNode域爲true,些時tail指向線程A的結點,然後線程B也加入到線程A後面,tail指向線程B的結點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"然後線程A和B都在它的myPred對象上旋轉,一旦它的myPred結點的locked字段變爲false,它就可以獲取鎖進行繼續執行業務方法。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"明顯線程A的myPred locked域爲false,此時線程A獲取到了鎖,如下圖所示。","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","marks":[{"type":"strong","attrs":{}}],"text":"從代碼中可以看出lock方法中有一個while循環,這 是在等待前趨結點的locked域變爲false,這是一個自旋等待的過程。unlock方法很簡單,只需要將自己的locked域設置爲false即可。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5a/5ab71c8e86f4415facdfec3fa7b9edf9.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":3},"content":[{"type":"text","text":"CLH優缺點","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"唯一的缺點是在NUMA系統結構下性能很差,在這種系統結構下,每個線程有自己的內存,如果前趨結點的內存位置比較遠,自旋判斷前趨結點的locked域,性能將大打折扣,但是在SMP系統結構下該法還是非常有效的。一種解決NUMA系統結構的思路是MCS隊列鎖。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章