synchronized關鍵字是控制java中線程同步的,能夠分爲synchronized塊和synchronized方法兩種狀況。弄懂具體功能及它們的異同仍是頗有必要的。java
一、同步方法ide
新建的測試類:測試
//the synchronized functions are in this class class Executer { public synchronized void execute1() { for (int i = 0; i < 3; ++i) { System.out.println(i); } } public synchronized void execute2() { for (int i = 0; i < 3; ++i) { System.out.println(i); } } } //thread1, it will invoke the function "execute1()" in Executer class Thread1 extends Thread { private Executer executer; public Thread1(Executer executer) { this.executer = executer; } @Override public void run() { executer.execute1();/ } } //thread2, it will invoke the function "execute2()" in Executer class Thread2 extends Thread { private Executer executer; public Thread2(Executer executer) { this.executer = executer; } @Override public void run() { executer.execute2(); } }
1.一、當兩個線程訪問同一個對象的同一個同步方法:this
main方法所在類:spa
public class SyncTest { public static void main(String[] args) { Executer executer = new Executer();//initiate only one instance Thread t1 = new Thread1(executer); Thread t2 = new Thread1(executer);//t1 and t2 are both instance of Thread1, and they will both invoke the function "execute1()". t1.start(); t2.start(); } }
輸出結果顯然是線程
0 1 2 0 1 2
t1先執行execute1()方法,給其上鎖。訪問完畢後解鎖,再由t2去訪問。而若是execute1()不加synchronized方法的話,輸出將會是00 11 22,也就是t1, t2兩個線程同時訪問execute1()方法。如今咱們須要知道這個「鎖」是上在哪裏的。code
1.二、當兩個線程訪問同一個對象的不一樣同步方法:orm
main方法所在類:對象
public class SyncTest2 { public static void main(String[] args) { Executer executer = new Executer();//initiate only one instance Thread t1 = new Thread1(executer); Thread t2 = new Thread2(executer);//t1 will invoke the function "execute1()". And t2 will invoke "execute2()". t1.start(); t2.start(); } }
結果執行輸出爲:
同步
0 1 2 0 1 2
跟上例的結果同樣。而咱們這裏兩個線程訪問的明明是兩個不一樣的方法。這說明當有線程訪問同步方法(synchronized)時,會給對象上鎖,而不是方法。上鎖期間,其餘線程不能訪問該對象的任何同步方法。直到此線程結束或者拋異常,纔會把鎖釋放。再來進一步研究鎖的級別。
1.三、靜態方法的同步
將測試類做以下修改(將Executer中的方法改成靜態,兩個Thread類不變):
class Executer { public synchronized static void execute1() {//static synchronized for (int i = 0; i < 3; ++i) { System.out.println(i);//print i. } } public synchronized static void execute2() {//static synchronized for (int i = 0; i < 3; ++i) { System.out.println(i);//print i. } } }
而後在main方法中如是輸入:
public class SyncTest2 { public static void main(String[] args) { Executer executer1 = new Executer(); Executer executer2 = new Executer();//initiate two instances Thread t1 = new Thread1(executer1);//invoke execute1() Thread t2 = new Thread2(executer2);//invoke execute2() t1.start(); t2.start(); } }
輸出結果:
0 1 2 0 1 2
依然同步。這裏咱們建立了兩個對象實例,按理說不一樣線程訪問各自對象的同步方法的時候,兩個線程間應該互不影響。然而,結果卻說明它們之間會有制約關係。問題就出在static關鍵字。被static修飾的方法爲類方法,而非是對象層面的。當方法同時被static synchronized關鍵字修飾,線程訪問此方法時會給這個類上鎖,而不單單是對象。期間其餘線程沒法訪問這個類的全部靜態同步方法。
2.同步塊
其實也能夠將Executer類中的方法以下寫:
class Executer { private Object object = new Object(); public void execute1() { synchronized (object) { for (int i = 0; i < 3; ++i) { System.out.println(i); } } } public void execute2() { synchronized (object) { for (int i = 0; i < 3; ++i) { System.out.println(i); } } } }
實現的效果跟以前的1.2例同樣。原理就是對object對象加上鎖,同時只能有一個線程能夠訪問該方法。
而後如今有這樣一個問題:
public class SyncTest extends Thread { private static String str = ""; private String type = ""; private static void method(String s) { synchronized (str) { /************ //str = s; ************/ System.out.println(s); while(true){ } } } public void method1() { method("method1"); } public static void staticMethod1() { method("staticMethod1"); } public void run() { if(type.equals("static")) { staticMethod1(); } else{ method1(); } } public SyncTest(String type) { this.type = type; } public static void main(String[] args) { SyncTest1 test1 = new SyncTest1("nonstatic"); SyncTest1 test2 = new SyncTest1("static"); test1.start(); test2.start(); } }
問註釋那一行加與不加什麼區別?
如下是解開註釋的結果:
method1 staticMethod1
而後陷入while無限循環。
如下是註釋掉的結果:
method1
也是陷入了while無限循環。
其實產生這個差異的主要緣由是synchronized塊裏的object改變了。註釋那行是修改了此object。若是加上這一行,會致使從新產生一個String對象,此方法快的同步性將被破壞,因而出現了兩個方法同時訪問這個「同步塊」的狀況。
順便說一下,同步塊裏的參數只能是對象(object),不能是基本類型的數據(如int,byte)。
因爲同步塊的控制比較細粒度,並且能夠傳this關鍵字進去(至關於當前對象的一個synchronized修飾的方法),因此使用上可能會更普遍一點吧。