线程同步synchronized理解
Synchronized 理解
用法:1.同步方法。2.同步靜態方法。3同步代碼塊。
理解Synchronized 的關鍵是“鎖” (原理在最后)
同步代碼有“鎖”者執行。所謂的鎖必須是同一個。靜態的方法是該類的.class ,而非靜態的或代碼塊指的是同一個對象。
來說說不同情況下的鎖的情形。
一:類中有普通同步方法?
package test;public class Test {public synchronized void fun() {System.out.println("fun start");try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun end");}public static void main(String[] args) {Test test = new Test();Thread t = new Thread(new Runnable() {@Overridepublic void run() {test.fun();}});t.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {test.fun();}});t2.start();}}顯然是同步執行的。就是t線程執行結束,t1才能進度,沒有什么異議。
現在做一個改變
1 package test; 2 3 public class Test { 4 public synchronized void fun() { 5 System.out.println("fun start"); 6 try { 7 Thread.sleep(1000); 8 } catch (InterruptedException e) { 9 e.printStackTrace(); 10 } 11 System.out.println("fun end"); 12 } 13 14 public synchronized void funNoSyc() { 15 System.out.println("funNoSyc start"); 16 try { 17 Thread.sleep(1000); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 System.out.println("funNoSyc end"); 22 } 23 24 public static void main(String[] args) { 25 26 Test test = new Test(); 27 Test test1 = new Test(); 28 Thread t = new Thread(new Runnable() { 29 30 @Override 31 public void run() { 32 test.fun(); 33 } 34 }); 35 t.start(); 36 Thread t2 = new Thread(new Runnable() { 37 38 @Override 39 public void run() { 40 test1.fun(); 41 } 42 }); 43 t2.start(); 44 45 } 46 47 } View Code?
?
Test new了兩個不同對象,看一下執行結果。
?
可以看到同時執行的,根本沒有同步。原是,兩個對象,不是同一把“鎖” ,很好理解吧。
結論:方法的同步對使用了同一同步類(同一鎖)的操作起作用。
?
接下來再變一下,如果類同有多個同步方法,調用過程會是啥樣?
兩個同步方法,1和2。使用同一個對象"test".線程1調用fun 線程2調fun1 .看結果。代碼下。
package test;public class Test {public synchronized void fun() {System.out.println("fun start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun end");}public synchronized void fun1() {System.out.println("fun1 start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun1 end");}public static void main(String[] args) {Test test = new Test();Thread t = new Thread(new Runnable() {@Overridepublic void run() {test.fun();}});t.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {test.fun1();}});t2.start();}}?
運行結果
結果可以看出,運行fun的時候,fun1不能運行,只能等待。
結論:方法上加?synchronized 隱式加了synchronize(this).所以同一個類中,不同的同步方法使用的是同一個鎖,只要一個同步方法在執行,其它方法只能等待。
?
二:類中同步的靜態方法
?看代碼
package test;public class Test {public static synchronized void fun() {System.out.println("fun start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun end");}public static void main(String[] args) {Test test = new Test();Thread t = new Thread(new Runnable() {@Overridepublic void run() {Test.fun();}});t.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {Test.fun();}});t2.start();}}運行結果 是同步的。先后執行。
?
改一下,用不同的對象去執行:
?
最后結果一樣的。
?
如果類中有多個靜態方法
package test;public class Test {public static synchronized void fun() {System.out.println("fun start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun end");}public static synchronized void fun1() {System.out.println("fun1 start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun1 end");}public static void main(String[] args) {Test test = new Test();Test test1 = new Test();Thread t = new Thread(new Runnable() {@Overridepublic void run() {test.fun();}});t.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {test1.fun1();}});t2.start();}}執行結果:
一個靜態方法在執行,另一個則等待。
結論:所有靜態法都使用的同一個鎖,就是該類的.class。所以所有靜態方法都是互斥的。開發上要好好考慮,會有性能問題。
這就所謂的類鎖,其實就是所有靜態方法使用的同一個鎖。
?
三:同步代碼塊
synchronized (鎖){代碼
}
?到這兒其實都不用試了,這里面的代碼同步關鍵還是這個鎖。同鎖(同一對象)互斥。
?
package test;public class Test {public void fun() {synchronized (this) {System.out.println("fun start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun end");}}public static void main(String[] args) {Test test = new Test();// Test test1 = new Test();Thread t = new Thread(new Runnable() {@Overridepublic void run() {test.fun();}});t.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {//同一對象共互斥 test.fun();// test1.fun(); }});t2.start();}}結果:
fun start
fun end
fun start
fun end
?
?不同對象,不起作用。看下面代碼結果
package test;public class Test {public void fun() {synchronized (this) {System.out.println("fun start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun end");}}public static void main(String[] args) {Test test = new Test();Test test1 = new Test();Thread t = new Thread(new Runnable() {@Overridepublic void run() {test.fun();}});t.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// 同一對象共互斥// test.fun(); test1.fun();}});t2.start();}}結果:
fun start
fun start
fun end
fun end
?
?或者修改代碼如下:
傳入一個對象鎖
package test;public class Test {public void fun(Object object) {synchronized (object) {System.out.println("fun start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("fun end");}}public static void main(String[] args) {Test test = new Test();Test test1 = new Test();Thread t = new Thread(new Runnable() {@Overridepublic void run() {test.fun(test);}});t.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {test1.fun(test);}});t2.start();}}這樣的代碼塊是在不同的對象,但是傳的是同一個對象鎖,結果照樣還是同步的。
?
?
總結:synchronized 工作的關鍵就是這個對象鎖。靜態的方法所用鎖就是類.class,所有人共用,因為只有一個,非靜態的就是具體的對象。
同步只對使用的同一個鎖的過程起作用。
?
Synchronized 原理
如果對上面的執行結果還有疑問,也先不用急,我們先來了解Synchronized的原理,再回頭上面的問題就一目了然了。我們先通過反編譯下面的代碼來看看Synchronized是如何實現對代碼塊進行同步的:
1 package com.paddx.test.concurrent; 2 3 public class SynchronizedDemo { 4 public void method() { 5 synchronized (this) { 6 System.out.println("Method 1 start"); 7 } 8 } 9 }反編譯結果:
關于這兩條指令的作用,我們直接參考JVM規范中描述:
monitorenter :
Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
? If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
? If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
? If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.
這段話的大概意思為:
每個對象有一個監視器鎖(monitor)。當monitor被占用時就會處于鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:
1、如果monitor的進入數為0,則該線程進入monitor,然后將進入數設置為1,該線程即為monitor的所有者。
2、如果線程已經占有該monitor,只是重新進入,則進入monitor的進入數加1.
3.如果其他線程已經占用了monitor,則該線程進入阻塞狀態,直到monitor的進入數為0,再重新嘗試獲取monitor的所有權。
monitorexit:
The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.
這段話的大概意思為:
執行monitorexit的線程必須是objectref所對應的monitor的所有者。
指令執行時,monitor的進入數減1,如果減1后進入數為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去獲取這個 monitor 的所有權。?
通過這兩段描述,我們應該能很清楚的看出Synchronized的實現原理,Synchronized的語義底層是通過一個monitor的對象來完成,其實wait/notify等方法也依賴于monitor對象,這就是為什么只有在同步的塊或者方法中才能調用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。
我們再來看一下同步方法的反編譯結果:
源代碼:
1 package com.paddx.test.concurrent; 2 3 public class SynchronizedMethod { 4 public synchronized void method() { 5 System.out.println("Hello World!"); 6 } 7 }反編譯結果:
從反編譯的結果來看,方法的同步并沒有通過指令monitorenter和monitorexit來完成(理論上其實也可以通過這兩條指令來實現),不過相對于普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。JVM就是根據該標示符來實現方法的同步的:當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標志是否被設置,如果設置了,執行線程將先獲取monitor,獲取成功之后才能執行方法體,方法執行完后再釋放monitor。在方法執行期間,其他任何線程都無法再獲得同一個monitor對象。 其實本質上沒有區別,只是方法的同步是一種隱式的方式來實現,無需通過字節碼來完成
轉載于:https://www.cnblogs.com/mamamia/p/8005922.html
總結
以上是生活随笔為你收集整理的线程同步synchronized理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据库——索引
- 下一篇: bzoj1207: [HNOI2004]