執行緒同步與synchronized
看如下程式碼:
package project; public class Demo { public static void main(String []args){ final Out out=new Out(); new Thread(){ public void run(){ int i=0; while(i<5){ i++; out.print("1111111111"); } } }.start(); new Thread(){ public void run(){ int i=0; while(i<5){ i++; out.print("2222222222"); } } }.start(); } } class Out{ public void print(String str){ for(int i=0;i<str.length();i++){ System.out.print(str.charAt(i)); } System.out.println(); } }
執行結果:
理想的列印應該是每行只有1或2並且每行10個數字,而上面卻出現了行內數字個數不為10,1中還夾雜著2。
那麼出現這種現象的原因是什麼呢?
執行緒並不是同時執行的,而是CPU快速的在這執行緒之間切換執行,由於切換速度極快使我們感覺同時執行罷了。
在這裡,A執行緒在還沒有執行完print()方法時,就切換到了B執行緒呼叫了print(),然後在B執行緒結束或者還沒結束又切換到了A執行緒,如此反覆,造成的結果。
那麼怎麼控制在A(或B)執行緒在呼叫print()方法時,另一個執行緒不呼叫print()方法呢?
針對執行緒同步問題java早就有解決方法了,最簡單的就是給方法加上synchronized
class Out{
public synchronized void print(String str){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
這樣結果就會是理想狀態的結果了:
這是什麼意思呢?加上synchronized關鍵字後,比如A執行緒執行print()方法就相當於拿到了一把鎖,只有獲取這個鎖才能執行此方法,如果在A執行緒執行print()方法過程中B執行緒也想插一腳進來執行print()方法,對不起此時這是不能夠的,因為此時鎖在A執行緒手裡,B執行緒無權拿到這把鎖,只有等到A執行緒執行完後放棄鎖,B執行緒才能拿到鎖執行print()方法。
為print()方法加上synchronized後其就變成了同步方法,普通同步方法的鎖是this,也就是當前物件,比如Demo中,外部要想呼叫out方法就必須建立Out類例項物件out,此時print()同步方法的鎖就是這個out。
我們還可以通過同步程式碼塊
寫法:synchronized(obj){},其中obj為鎖物件。
如這裡,修改Out類:
class Out{
public void print(String str){
synchronized(this){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
}
這樣結果還會是理想狀態:
原因是在執行同步程式碼塊裡面的內容的執行緒 獲得了鎖,只有在它執行完畢後釋放了鎖,其他執行緒才能獲得鎖,並執行程式碼塊裡面的內容。
如上是對一個類的一個物件,裡面的方法或者程式碼塊加鎖。
如果是一個類的多個物件呢?會出現什麼情況?看下面一段程式碼:
package project;
public class Demo {
public static void main(String []args){
final Out out1=new Out();
final Out out2=new Out();
new Thread(){
public void run(){
int i=0;
while(i<5){
i++;
out1.print("1111111111");
}
}
}.start();
new Thread(){
public void run(){
int i=0;
while(i<5){
i++;
out2.print("2222222222");
}
}
}.start();
}
}
class Out{
public void print(String str){
synchronized(this){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
}
上面的程式碼了,建立了兩個Out類物件,兩個執行緒分別執行不同的物件的print()方法 。
看下執行結果:
很顯然,結果並不理想,synchronized 加鎖並沒起作用。
原因:synchronized 的鎖物件為this,也就是當前例項物件,但這裡是同一個類的不同物件例項,兩個執行緒訪問的不是同一個物件,所以synchronized並不會起作用。
那麼這裡要怎麼讓synchronized起作用呢?
讓synchronized鎖這個類對應的Class物件。
修改Out類:
class Out{
public void print(String str){
synchronized(Out.class){
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i));
}
System.out.println();
}
}
}
以上將synchronized(this)改為了synchronized(Out.class)
執行結果如下:
這樣加鎖後,就算不是同一個類的例項物件,鎖也能起作用。