目錄
問題描述
1 鎖方法
2 鎖代碼塊
3 鎖某個類
4 靜態方法上的synchronized
當我們處理多線程處理同步問題的時候就會用到synchronized這個關鍵字,下面介紹下synchronized的四種用法。
問題描述
介紹之前我們先來看下,在java 多線程中 如果沒有線程同步會出現什么問題:
下面這個是一個測試例子:
public class MainClass {public static class MyRun implements Runnable{private int count=0;@Overridepublic void run() {while (count<15){System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");count++;try {Thread.sleep(200);}catch (Exception e){}}}}public static void main(String args[]){ MyRun myRun=new MyRun();Thread threadA=new Thread(myRun,"A");Thread threadB=new Thread(myRun,"B");threadA.start();threadB.start();}}
運行結果:
ThreadName:A? count:0
ThreadName:B? count:0
ThreadName:A? count:1
ThreadName:A? count:3
ThreadName:B? count:2
ThreadName:A? count:4
ThreadName:A? count:6
ThreadName:A? count:7
ThreadName:B? count:5
我們看到這個count在無序的增加,這個是由于A,B兩個線程同時操作Count變量造成的,如果我們想讓Count有序增加,應該給
System.out.print("ThreadName:"+Thread.currentThread().getName()+"? count:"+count+"\n");
??????????????????? count++;
這段代碼同步枷鎖,這樣當B線程進入這里時候,發現這里已經被鎖,就只有等待,A執行完這段代碼之后就會釋放對這個對象的這段代碼的釋放鎖,A獲得了釋放鎖,就可以進入執行,讓A,B有序進入執行,才能讓Count有序增加,加入了synchronized之后的代碼:
while (count<15) {synchronized (this){System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");count++;}try {Thread.sleep(200);}catch (Exception e){}}
結果:
ThreadName:A? count:0
ThreadName:B? count:1
ThreadName:B? count:2
ThreadName:A? count:3
ThreadName:A? count:4
ThreadName:B? count:5
ThreadName:B? count:6
ThreadName:A? count:7
ThreadName:B? count:8
加了鎖之后A,B線程就可以有序的交替執行,不會同時搶占執行Count++ 操作,
下面介紹synchronised的幾種用法。
1 鎖方法
public synchronized void dodo(){ }
這個就是鎖方法,這里面要注意兩點:
synchronized 關鍵子不是方法的一部分,所以它不會被繼承,說白了,就是如果父類的方法有synchronized,子類重寫這個方法,synchronized不寫也不會報錯
synchronized 不能修飾接口
synchronized 不能修復構造方法,但是可以修飾構造方法里面的代碼塊
2 鎖代碼塊
鎖代碼塊就是我上面的那個例子寫法了,這個就是鎖的是某個對象中的某個代碼,讓它線程同步。
?
synchronized (this) {System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");count++;}
我們看到這個synchronized (this) 里面的this,這里是要傳入一個對象的,如果不用this也可以,也可以在這個類里面new一個其他對象效果也是一樣的,比如
??
private Object obj=new Object();@Overridepublic void run() {while (count<15){synchronized (obj){System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");count++;}try {Thread.sleep(200);}catch (Exception e){}}}
這里就用了obj這個new的對象,效果和this完全一樣
3 鎖某個類
public class MainClass {public static class MyRun implements Runnable{public static int count=0;@Overridepublic void run() {while (count<15){synchronized (this){System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");count++;}try {Thread.sleep(200);}catch (Exception e){}}}public synchronized void dodo(){}}public static void main(String args[]){MyRun myRun1=new MyRun();MyRun myRun2=new MyRun();Thread threadA=new Thread(myRun1,"A");Thread threadB=new Thread(myRun2,"B");threadA.start();threadB.start();}}
這個代碼里面,
?MyRun myRun1=new MyRun();
?MyRun myRun2=new MyRun();
我new兩個MyRun,我前面說過synchronized(this)只能鎖某個對象,就是說threadA執行myRun1 threadB執行myRun2,互不干擾,synchronized只能鎖自己的run1 或者run2 不能兩個對象同時鎖到,所以執行的結果是無序的。
ThreadName:A? count:0
ThreadName:B? count:0
ThreadName:B? count:2
ThreadName:A? count:2
ThreadName:B? count:4
ThreadName:A? count:4
ThreadName:A? count:6
ThreadName:B? count:6
ThreadName:A? count:8
ThreadName:B? count:8
如果我們想讓兩個線程有序執行,這個Count++操作,而且對run1,和run2都同時鎖,應該怎么辦呢???
答案是鎖類,鎖類的意思是不管是這個類new了多少對象,這個對象的所有方法,都會上鎖,我們更改下代碼看看結果,怎么鎖類的:
?synchronized (MyRun.class) {
??????????????????????? System.out.print("ThreadName:"+Thread.currentThread().getName()+"? count:"+count+"\n");
??????????????????????? count++;
??????????????????? }
我們看到只改動了 synchronized (MyRun.class)這里,其他代碼都不變們,這個就把這個MyRun鎖了,我們看看結果:
ThreadName:A? count:0
ThreadName:B? count:1
ThreadName:B? count:2
ThreadName:A? count:3
ThreadName:B? count:4
ThreadName:A? count:5
ThreadName:A? count:6
ThreadName:B? count:7
ThreadName:A? count:8
結果是有序的,驗證了我們的結果
4 靜態方法上的synchronized
public synchronized? static void ddo()
這種方式其實和第三種效果是一樣的,都是對類起作用,因為我們知道static修飾的方法是類方法,所以這個synchronized 也是作用于整個類
我們把上面的代碼改下看看效果是不是一樣:
public class MainClass {public static class MyRun implements Runnable{public static int count=0;@Overridepublic void run() {ddo();}public synchronized static void ddo(){while (count<15){System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");count++;try {Thread.sleep(200);}catch (Exception e){}}}}public static void main(String args[]){MyRun myRun1=new MyRun();MyRun myRun2=new MyRun();Thread threadA=new Thread(myRun1,"A");Thread threadB=new Thread(myRun2,"B");threadA.start();threadB.start();}}
結果:
ThreadName:A? count:0
ThreadName:A? count:1
ThreadName:A? count:2
ThreadName:A? count:3
ThreadName:A? count:4
ThreadName:A? count:5
ThreadName:A? count:6
ThreadName:A? count:7
ThreadName:A? count:8
因為A線程 先執行最后滿足條件 while (count<15),所以B沒有機會執行了,驗證符合我們的預期
總結
1、鎖如果加在方法上面,或者在方法中的代碼塊形式,就是鎖的這個對象,如果鎖是靜態方法中,或者代碼塊synchronized(A.class) 形式 就是鎖的這個類,里面的所有方法都會同步。
2、誰擁有了鎖,上面線程就擁有了控制這段代碼的能力,其他的線程只能看著,只有釋放了鎖,其他線程才可以操作。
3、synchronized 消耗系統性能,所以能不加鎖的邏輯,盡量不要加。
4、操作讀寫文件,或者數據庫,有的時候多線程會出現不可預知的問題,所以要加入鎖。