Synchronized 精講

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.簡介","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.1 作用","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":"text","marks":[{"type":"strong","attrs":{}}],"text":"併發場景","attrs":{}},{"type":"text","text":"中,保證","attrs":{}},{"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":"text","marks":[{"type":"strong","attrs":{}}],"text":"併發隱患的代碼","attrs":{}},{"type":"text","text":"進行操作","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.2 錯誤案例","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":"需求","attrs":{}},{"type":"text","text":":兩個線程對 count 變量進行200000次循環增加,預期結果是400000次","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SynchronizedDemo implements Runnable {\n private static int count = 0;\n static SynchronizedDemo synchronizedInstance = new SynchronizedDemo();\n public static void main(String[] args) {\n Thread t1 = new Thread(synchronizedInstance);\n Thread t2 = new Thread(synchronizedInstance);\n t1.start();\n t2.start();\n try {\n t1.join();\n t2.join();\n System.out.println(\"count 最終的值爲: \" + count);\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n }\n @Override\n public void run() {\n synchronized (this) {\n for (int i = 0; i < 200000; i++) {\n count++;\n }\n }\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"結果 :顯然不等於400000次所以出現了運算錯誤","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/21/2173ec5ef30f383dd0d67bec431b13b5.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"原因:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" count++;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該語句包含三個操作:","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"線程t1、t2 從主內存中獲取共享變量count的值,到自己的工作內存中","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"將自己的工作內存中的count值進行+1操作","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"將修改完的count變量的值存入到主內存中","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/25/25a9739829627a42b1a5a938c39fa2f2.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意:他們是將自己工作內存中的值進行改變刷回主內存,假設當前count的值爲8,t1、t2將count的值複製到自己的工作內存中進行修改,如果此時t1將count變成9、t2此時也將count的值變成9,當t1、t2兩個線程都將值刷回主內存的時候count值爲9,並不是10,這個時候就會造成最後的結果和預期的不一致。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.3 正確案例","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼塊上加對象鎖 this","attrs":{}}]}],"attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Override\npublic void run() {\n synchronized (this) {\n for (int i = 0; i < 200000; i++) {\n count++;\n }\n }\n}","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在普通方法上加鎖","attrs":{}}]}],"attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Override\npublic synchronized void run() {\n for (int i = 0; i < 200000; i++) {\n count++;\n }\n}","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"3","normalizeStart":"3"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"加.class鎖","attrs":{}}]}],"attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Override\npublic void run() {\n for (int i = 0; i < 200000; i++) {\n synchronized (SynchronizedDemo.class) {\n count++;\n }\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輸出結果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d7/d7b8f40bcf34b6f4b936c48d0c2a7e0f.png","alt":"在這裏插入圖片描述","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"後文詳細講解四種加 synchronized 的方式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.用法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 對象鎖","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":"2.1.1 方法鎖","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":"修飾普通方法默認鎖對象爲this當前實例對象","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":"public ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"synchronized","attrs":{}},{"type":"text","text":" void method() ;在普通方法上面加synchronized","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SynchronizedDemo3 implements Runnable {\n static SynchronizedDemo3 synchronizedDemo3 = new SynchronizedDemo3();\n public synchronized void method() {\n System.out.println(\"線程名稱\" + Thread.currentThread().getName());\n try {\n Thread.sleep(3000);\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n System.out.println(\"線程名稱\" + Thread.currentThread().getName() + \"運行完成\");\n }\n @Override\n public void run() {\n method();\n }\n public static void main(String[] args) {\n Thread t1 = new Thread(synchronizedDemo3);\n t1.setName(\"我是線程 t1\");\n Thread t2 = new Thread(synchronizedDemo3);\n t2.setName(\"我是線程 t2\");\n t1.start();\n t2.start();\n try {\n t1.join();\n t2.join();\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輸出結果:","attrs":{}},{"type":"text","text":" 線程 t1 和線程 t2 執行過程是順序執行的","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3b/3bd93d830a6393a3bfec95d256195c93.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.1.2 同步代碼塊","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼示例","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/f6/f6a5ae9cd664a300ea3d1b717773741e.png","alt":"在這裏插入圖片描述","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":"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":"輸出結果","attrs":{}},{"type":"text","text":":線程 t1 和線程 t2 交叉執行形成了亂序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f3/f36311aedde0d040c808bf3e83ee010d.png","alt":"圖片","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":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼示例","attrs":{}},{"type":"text","text":":加Synchronized 鎖而定義的兩個線程執行的情況,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"鎖對象的是this(當前對象)","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/71/71ef4c22c07605ac0ee8dc0992aeb7eb.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輸出結果","attrs":{}},{"type":"text","text":":線程 t1 和線程 t2 執行過程是順序執行的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9b/9b7b2739ea6d3240a0177c942b8aaad4.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"3","normalizeStart":"3"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼示例","attrs":{}},{"type":"text","text":":加Synchronized 鎖而定義的兩個線程執行的情況,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"鎖對象的是自定義對象","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fa/faef612abf587d2d8d91e6d77c2d1730.png","alt":"在這裏插入圖片描述","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":"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":"輸出結果","attrs":{}},{"type":"text","text":":線程 t1 和線程 t2 執行形成了順序,這種情況下和this沒有什麼區別,但是如果","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"是多個同步代碼塊的話就需要進行自定義對象鎖","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/1f/1fc095be5b75cf0b204a78b8bc517ddc.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"4","normalizeStart":"4"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼示例:多個同步代碼塊使用自定義對象鎖,(兩個自定義對象鎖對應兩個同步代碼塊)","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b8/b82632a98581257d2a0ecda17307e5c6.png","alt":"在這裏插入圖片描述","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":"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":"輸出結果","attrs":{}},{"type":"text","text":":輸出順序線程t1 和線程t2 代碼進行了交叉執行,出現了亂序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/34/34556622416f2b6092df2ab1282659e0.png","alt":"在這裏插入圖片描述","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":"numberedlist","attrs":{"start":"5","normalizeStart":"5"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼示例:多個同步代碼塊使用自定義對象鎖,(一個自定義對象鎖對應兩個同步代碼塊)","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d6/d6ba2461021eb63b04fbfb7c32ade967.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輸出結果","attrs":{}},{"type":"text","text":":線程 t1 和線程 t2 執行形成了順序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/22/22123e73827bdcd87e7c984d1d646708.png","alt":"圖片","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":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 類鎖","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":"特點:類鎖只能在同一時間被一個對象擁有(無論有多少個實例想訪問也是一個對象持有它)","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":"2.2.1 synchronized修飾靜態的方法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼示例:","attrs":{}},{"type":"text","text":" synchronized 加在普通方法上面","attrs":{}}]}],"attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SynchronizedDemo4 implements Runnable {\n private static SynchronizedDemo4 synchronizedInstance1 = new SynchronizedDemo4();\n private static SynchronizedDemo4 synchronizedInstance2 = new SynchronizedDemo4();\n public synchronized void method() {\n System.out.println(\"線程名稱\" + Thread.currentThread().getName());\n try {\n Thread.sleep(3000);\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n System.out.println(\"線程名稱\" + Thread.currentThread().getName() + \"運行完成\");\n }\n @Override\n public void run() {\n method();\n }\n public static void main(String[] args) {\n Thread t1 = new Thread(synchronizedInstance1);\n t1.setName(\"我是線程 t1\");\n Thread t2 = new Thread(synchronizedInstance2);\n t2.setName(\"我是線程 t2\");\n t1.start();\n t2.start();\n try {\n t1.join();\n t2.join();\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輸出結果","attrs":{}},{"type":"text","text":":輸出順序線程t1 和線程t2 代碼進行了交叉執行,出現了亂序","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/56/56430e0e83d8b1e97a941dbe857b2696.png","alt":"圖片","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":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼示例:","attrs":{}},{"type":"text","text":" synchronized 加在靜態方法上面","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":"public ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"static synchronized","attrs":{}},{"type":"text","text":" void method();使用方式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SynchronizedDemo4 implements Runnable {\n private static SynchronizedDemo4 synchronizedInstance1 = new SynchronizedDemo4();\n private static SynchronizedDemo4 synchronizedInstance2 = new SynchronizedDemo4();\n public static synchronized void method() {\n System.out.println(\"線程名稱\" + Thread.currentThread().getName());\n try {\n Thread.sleep(3000);\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n System.out.println(\"線程名稱\" + Thread.currentThread().getName() + \"運行完成\");\n }\n @Override\n public void run() {\n method();\n }\n public static void main(String[] args) {\n Thread t1 = new Thread(synchronizedInstance1);\n t1.setName(\"我是線程 t1\");\n Thread t2 = new Thread(synchronizedInstance2);\n t2.setName(\"我是線程 t2\");\n t1.start();\n t2.start();\n try {\n t1.join();\n t2.join();\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輸出結果","attrs":{}},{"type":"text","text":":線程 t1 和線程 t2 執行形成了順序","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/79/792cbe01c26bd488e3d446e361ecde6f.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.2.2 指定鎖對象爲Class對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"代碼示例:","attrs":{}},{"type":"text","text":"synchronized 加","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":".class","attrs":{}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"synchronized (","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"SynchronizedDemo5.class","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":")","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SynchronizedDemo5 implements Runnable {\n private static SynchronizedDemo5 synchronizedInstance1 = new SynchronizedDemo5();\n private static SynchronizedDemo5 synchronizedInstance2 = new SynchronizedDemo5();\n void method() {\n synchronized (SynchronizedDemo5.class) { //類鎖只有一把\n System.out.println(\"線程名稱\" + Thread.currentThread().getName());\n try {\n Thread.sleep(3000);\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n System.out.println(\"線程名稱\" + Thread.currentThread().getName() + \"運行完成\");\n }\n }\n @Override\n public void run() {\n method();\n }\n public static void main(String[] args) {\n Thread t1 = new Thread(synchronizedInstance1);\n t1.setName(\"我是線程 t1\");\n Thread t2 = new Thread(synchronizedInstance2);\n t2.setName(\"我是線程 t2\");\n t1.start();\n t2.start();\n try {\n t1.join();\n t2.join();\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輸出結果:","attrs":{}},{"type":"text","text":" 線程 t1 和線程 t2 執行形成了順序","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/01/01a2303dc421e9033c8266301d3610fd.png","alt":"圖片","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":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.性質","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.1 可重入性也叫遞歸鎖","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"好處","attrs":{}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"粒度","attrs":{}},{"type":"text","text":":線程而非調用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.2案例證明可重入性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"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","marks":[{"type":"strong","attrs":{}}],"text":"代碼實例:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package synchronizedPage;\npublic class SynchronizedDemo6 {\n int count = 0;\n public static void main(String[] args) {\n SynchronizedDemo6 synchronizedDemo6 = new SynchronizedDemo6();\n synchronizedDemo6.method();\n }\n private synchronized void method() {\n System.out.println(count);\n if (count == 0) {\n count++;\n method();\n }\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輸出結果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/09/09ac3a93605e336a4e3a90b58cd91ca5.png","alt":"在這裏插入圖片描述","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"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","marks":[{"type":"strong","attrs":{}}],"text":"代碼實例:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package synchronizedPage;\npublic class SynchronizedDemo7 {\n private synchronized void method1() {\n System.out.println(\"method1\");\n method2();\n }\n private synchronized void method2() {\n System.out.println(\"method2\");\n }\n public static void main(String[] args) {\n SynchronizedDemo7 synchronizedDemo7 = new SynchronizedDemo7();\n synchronizedDemo7.method1();\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輸出結果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/eb/eb0502107b9b45c428591bd8b024d90e.png","alt":"在這裏插入圖片描述","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":"numberedlist","attrs":{"start":"3","normalizeStart":"3"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"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","marks":[{"type":"strong","attrs":{}}],"text":"代碼實例:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package synchronizedPage;\npublic class SynchronizedDemo8 {\n public synchronized void doSomething() {\n System.out.println(\"我是父類方法\");\n }\n}\nclass childrenClass extends SynchronizedDemo8{\n public synchronized void doSomething() {\n System.out.println(\"我是子類方法\");\n super.doSomething();\n }\n public static void main(String[] args) {\n childrenClass childrenClass = new childrenClass();\n childrenClass.doSomething();\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輸出結果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2a/2a264db17242564c82654139592ab650.png","alt":"圖片","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":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.3 不可中斷","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":"當A線程持有這把鎖時,B線程如果也想要A線程持有的鎖時只能等待,A永遠不釋放的話,那麼B線程永遠的等待下去。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4.底層原理實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4.1 加鎖和釋放鎖的原理","attrs":{}}]},{"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":"synchronized加在代碼塊上","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void test() {\n synchronized(this){\n count++;\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"利用 javap -verbose 類的名字查看編譯後的文件","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7e/7e24986317dd6426246652b8aa5122ba.png","alt":"在這裏插入圖片描述","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":"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":"monitorenter","attrs":{}},{"type":"text","text":":每個對象都是一個監視器鎖(monitor)。當monitor被佔用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"如果monitor的進入數爲0,則該線程進入monitor,然後將進入數設置爲1,該線程即爲monitor的所有者","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果線程已經佔有該monitor,只是重新進入,則進入monitor的進入數加1【","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"可重入性質","attrs":{}},{"type":"text","text":"】","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果其他線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數爲0,再重新嘗試獲取monitor的所有權","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":"monitorexit","attrs":{}},{"type":"text","text":":執行monitorexit的線程必須是objectref所對應的monitor的所有者。指令執行時,monitor的進入數減1,如果減1後進入數爲0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去獲取這個 monitor 的所有權","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":"monitorexit指令出現了兩次,第1次爲同步正常退出釋放鎖;第2次爲發生異步退出釋放鎖","attrs":{}}]},{"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":"synchronized加在方法上(無論時普通方法還是靜態方法)","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public synchronized void test() {\n count++;\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"利用 javap -verbose 類的名字查看編譯後的文件","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e2/e28c51937b8c82c43059431d74783058.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"方法的同步並沒有通過指令","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"monitorenter","attrs":{}},{"type":"text","text":"和monitorexit來完成,不過相對於普通方法,其常量池中多了","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"ACC_SYNCHRONIZED","attrs":{}},{"type":"text","text":"標示符。JVM就是根據該標示符來實現方法的同步的:","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":" 當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,如果設置了,執行線程將先獲取monitor,獲取成功之後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其他任何線程都無法再獲得同一個monitor對象,其實底層還是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"monitor對象鎖。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"5.Java虛擬機對synchronized的優化","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":"從JDK6開始,就對synchronized的實現機制進行了較大調整,包括使用JDK5引進的CAS自旋之外,還增加了自適應的CAS自旋、鎖消除、鎖粗化、偏向鎖、輕量級鎖這些優化策略。所以synchronized關鍵字的優化使得性能極大提高,同時語義清晰、操作簡單、無需手動關閉,所以推薦在允許的情況下儘量使用此關鍵字,同時在性能上此關鍵字還有優化的空間。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.1 鎖主要存在的四種狀態","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":"無鎖狀態、偏向鎖狀態、輕量級鎖狀態、重量級鎖狀態","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":"鎖的膨脹過程","attrs":{}},{"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","marks":[{"type":"strong","attrs":{}}],"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","marks":[{"type":"strong","attrs":{}}],"text":"只能從低到高升級,不會出現鎖的降級","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.2 自旋鎖","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":"text","marks":[{"type":"strong","attrs":{}}],"text":"自旋鎖","attrs":{}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"使用場景:","attrs":{}},{"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","marks":[{"type":"strong","attrs":{}}],"text":"缺點:","attrs":{}},{"type":"text","text":"雖然它可以避免線程切換帶來的開銷,但是它佔用了CPU處理器的時間。如果持有鎖的線程很快就釋放了鎖,那麼自旋的效率就非常好,反之,自旋的線程就會白白消耗掉處理的資源,它不會做任何有意義的工作,所以增加了","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"適應性自選鎖","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.3 適應性自旋鎖","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":"text","marks":[{"type":"strong","attrs":{}}],"text":"自旋的次數不再是固定的","attrs":{}},{"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":"線程如果自旋成功了,那麼下次自旋的次數會更加多,因爲上次成功了,那麼此次自旋也很有可能會再次成功,那麼它就會允許自旋等待持續的次數更多。反之,很少能夠成功,那麼以後自旋的次數會減少甚至省略掉自旋過程,以免浪費處理器資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.4 鎖消除","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":"爲了保證數據的完整性,在進行操作時需要對這部分操作進行同步控制,但是在有些情況下,JVM檢測到不可能存在共享數據競爭,這是JVM會對這些同步鎖進行鎖消除。作爲寫程序的人應該會知道哪裏存在數據競爭,不可能隨便的加鎖。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.5 鎖粗化","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":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.6 偏向鎖","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":"text","marks":[{"type":"strong","attrs":{}}],"text":"偏向鎖是在單線程執行代碼塊時使用的機制,如果在多線程併發的環境下(即線程A尚未執行完同步代碼塊,線程B發起了申請鎖的申請),則一定會轉化爲輕量級鎖或者重量級鎖。","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":"引入偏向鎖主要目的是","attrs":{}},{"type":"text","text":":爲了在沒有多線程競爭的情況下儘量減少不必要的輕量級鎖執行路徑。因爲輕量級鎖的加鎖解鎖操作是需要依賴多次CAS原子指令的,而偏向鎖只需要在置換ThreadID的時候依賴一次CAS原子指令。","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":"當一個線程訪問同步塊並獲取鎖時,會在對象頭和棧幀中的鎖記錄裏存儲鎖偏向的線程ID,以後該線程進入和退出同步塊時不需要花費CAS操作來爭奪鎖資源,只需要檢查是否爲偏向鎖、鎖標識爲以及ThreadID即可,處理流程如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"暫停擁有偏向鎖的線程","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"判斷鎖對象是否還處於被鎖定狀態,否,則恢復到無鎖狀態(01),以允許其餘線程競爭。是,則掛起持有鎖的當前線程,並將指向當前線程的鎖記錄地址的指針放入對象頭,升級爲輕量級鎖狀態(00),然後恢復持有鎖的當前線程,進入輕量級鎖的競爭模式","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":"偏向鎖的獲取和撤銷流程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/76/761d24d99a8447ed360991431c5c9bc2.png","alt":"圖片","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":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.7 輕量級鎖","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":"text","marks":[{"type":"strong","attrs":{}}],"text":"在沒有多線程競爭的前提下,減少傳統的重量級鎖使用操作系統互斥量產生的性能消耗","attrs":{}},{"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","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"在線程進入同步塊時,如果","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"同步對象鎖狀態爲無鎖狀態(鎖標誌位爲“01”狀態,是否爲偏向鎖爲“0”)","attrs":{}},{"type":"text","text":",虛擬機首先將在當前線程的棧幀中建立一個名爲鎖記錄(Lock Record)的空間,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"用於存儲鎖對象目前的Mark Word的複製","attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"拷貝對象頭中的Mark Word複製到鎖記錄(Lock Record)中。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"拷貝成功後,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"虛擬機將使用CAS操作嘗試將對象Mark Word中的Lock Word更新爲指向當前線程Lock Record的指針,並將Lock record裏的owner指針指向object mark word","attrs":{}},{"type":"text","text":"。如果更新成功,則執行步驟(4),否則執行步驟(5)。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"如果這個更新動作成功了,那麼當前線程就擁有了該對象的鎖,並且對象Mark Word的鎖標誌位設置爲“00”,即表示此對象處於輕量級鎖定狀態","attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"如果這個更新操作失敗了,虛擬機首先會檢查對象Mark Word中的Lock Word是否指向當前線程的棧幀,如果是,就說明當前線程已經擁有了這個對象的鎖,那就可以直接進入同步塊繼續執行。否則說明多個線程競爭鎖,進入自旋執行(3),","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"若自旋結束時仍未獲得鎖,輕量級鎖就要膨脹爲重量級鎖,鎖標誌的狀態值變爲“10”","attrs":{}},{"type":"text","text":",Mark Word中存儲的就是指向重量級鎖(互斥量)的指針,當前線程以及後面等待鎖的線程也要進入阻塞狀態。","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d293e6589dc01742953adbd0c540be70.png","alt":"圖片","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":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輕量級鎖的釋放也是通過CAS操作來進行的,","attrs":{}},{"type":"text","text":"主要步驟如下","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"通過CAS操作嘗試把線程中複製的Displaced Mark Word對象替換當前的Mark Word","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果替換成功,整個同步過程就完成了,恢復到無鎖狀態(01)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"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","marks":[{"type":"strong","attrs":{}}],"text":"問題:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"爲什麼升級爲輕量鎖時要把對象頭裏的Mark Word複製到線程棧的鎖記錄中呢?","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":"text","marks":[{"type":"strong","attrs":{}}],"text":"申請對象鎖時","attrs":{}},{"type":"text","text":"需要以該值作爲CAS的比較條件,同時在","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"升級到重量級鎖時","attrs":{}},{"type":"text","text":",能通過這個比較判定是否在持有鎖的過程中此鎖被其他線程申請過,如果被其他線程申請了,則在釋放鎖的時候要喚醒被掛起的線程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"爲什麼會嘗試CAS不成功以及什麼情況下會不成功?","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"CAS本身是不帶鎖機制的,其是通過比較來操作得","attrs":{}},{"type":"text","text":"。假設如下場景:線程A和線程B都在對象頭裏的鎖標識爲無鎖狀態進入,那麼如線程A先更新對象頭爲其鎖記錄指針成功之後,線程B再用CAS去更新,就會發現此時的對象頭已經不是其操作前的對象了,所以CAS會失敗。也就是說,只有兩個線程併發申請鎖的時候會發生CAS失敗。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"此時線程B進行CAS自旋","attrs":{}},{"type":"text","text":",等待對象頭的鎖標識重新變回無鎖狀態或對象頭內容等於對象,這也就意味着線程A執行結束,此時線程B的CAS操作終於成功了,於是線程B獲得了鎖以及執行同步代碼的權限。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"如果線程A的執行時間較長,線程B經過若干次CAS時鐘沒有成功,則鎖膨脹爲重量級鎖,即線程B被掛起阻塞、等待重新調度","attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.8 重量級鎖","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":"Synchronized是通過對象內部的一個叫做監視器鎖(Monitor)來實現的。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"但是監視器鎖本質又是依賴於底層的操作系統的Mutex Lock來實現的","attrs":{}},{"type":"text","text":"。而操作系統實現線程之間的切換這就需要從用戶態轉換到核心態,這個成本非常高,性能消耗特別嚴重。因此,這種依賴於操作系統Mutex Lock所實現的鎖我們稱之爲 “重量級鎖”。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"6. 缺點","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"效率低","attrs":{}}]}],"attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鎖的釋放情況少","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"試圖獲取鎖時不能設定超時","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不能中斷一個正在試圖獲得鎖的線程","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"不夠靈活","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 加鎖和釋放鎖的時候單一,每個鎖僅有一個單一條件","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"3","normalizeStart":"3"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"不知道是否成功獲取鎖","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章