線程同步方法

用synchronized關鍵字修飾同步方法 java

同步有幾種實現方法,都是什麼? 多線程

wait():使一個線程處於等待狀態,而且釋放所持有的對象的lock。 oop

sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉InerrupteException異常。 this

notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待線程,而是由JVM肯定喚醒哪一個線程,而不是按優先級。 spa

Allnotify():喚醒全部處於等待狀態的線程,注意並非給全部喚醒線程一個對象的鎖,而是讓它們競爭。 線程

同步是多線程中的重要概念。 設計

同步的使用能夠保證在多線程運行的環境中,程序不會產生設計以外的錯誤結果。同步的實現方式有兩種,同步方法和同步塊,這兩種方式都要用到synchronized關鍵字。 code

給一個方法增長synchronized修飾符以後使之成爲同步方法,這個方法能夠是靜態方法和非晶態方法,但不恩可以是抽象類的抽象方法,也不能是接口中的接口方法。 對象

public synchronized void aMethod() {   接口

    // do something  

}  

public static synchronized void anotherMethod() {  

    // do something  

}  

線程在執行同步方法時是具備排他性的,當任意一個線程既然怒到一個對象的人一個同步方法時,這個對象的全部同步方法都被鎖定了,在此期間,其餘任何線程都不能訪問這個對象的任一個同步方法,知道這個線程執行完它所電泳的同步方法並從中退出,從而致使它釋放了該對象的同步鎖以後。在一個對象被某一個線程鎖定以後,其餘線程是能夠訪問這個對象的全部非同步方法的。

同步塊是經過鎖定一個指定的對象,來對同步塊中包含的代碼進行同步;同步方法是對這個方法塊裏的代碼進行同步,而這種狀況下鎖定的對象就是同步方法所屬的主體對象自身。若是這個方法是靜態同步方法呢?那麼

那麼線程鎖定的就不是這個類的對象了,也不是這個類自身,而是這個類對應的java.lang.Class類型的對象。同步方法和同步塊之間的相互制約只限於同一個對象之間,因此靜態同步方法只受它所屬類的其它靜態同步方法的制約,而跟這個類的實例(對象)沒有關係。 

若是一個對象既有同步方法,又有同步塊,那麼當其中任意一個同步方法或者同步塊被某個線程執行時,這個對象就被鎖定了,其餘線程沒法在此時訪問這個對象的同步方法,也不能執行同步塊。

synchronized 關鍵字用於保護共享數據。請你們注意「共享數據」,你必定要分清哪些數據是共享數據,

  public class ThreadTest implements Runnable{ 
  public synchronized void run(){ 
  for(int i=0;i<10;i++) { 
  System.out.print(" " + i); 
  } 
  } 
  public static void main(String[] args) { 
  Runnable r1 = new ThreadTest(); //也可寫成ThreadTest r1 = new ThreadTest();
  Runnable r2 = new ThreadTest(); 
  Thread t1 = new Thread(r1); 
  Thread t2 = new Thread(r2); 
  t1.start(); 
  t2.start(); 
  }}

在這個程序中,run()雖然被加上了synchronized 關鍵字,但保護的不是共享數據。由於這個程序中的t1,t2 兩個對象(r1,r2)的線程。而不一樣的對象的數據是不一樣的,r1,r2 有各自的run()方法,因此輸出結果沒法預知

  public class ThreadTest implements Runnable{ 
  public synchronized void run(){ 
  for(int i=0;i<10;i++){ 
  System.out.print(" " + i); 
  } 
  } 
  public static void main(String[] args){ 
  Runnable r = new ThreadTest(); 
  Thread t1 = new Thread(r); 
  Thread t2 = new Thread(r); 
  t1.start(); 
  t2.start(); 
  }}

若是你運行1000 次這個程序,它的輸出結果也必定每次都是:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9。由於這裏的synchronized 保護的是共享數據。t1,t2 是同一個對象(r)的兩個線程,當其中的一個線程(例如:t1)開始執行run()方法時,因爲run()synchronized保護,因此同一個對象的其餘線程(t2)沒法訪問synchronized 方法(run 方法)。只有當t1執行完後t2 纔有機會執行。

  public class ThreadTest implements Runnable{ 
  public void run(){ 
  synchronized(this){ 
  for(int i=0;i<10;i++){ 
  System.out.print(" " + i); 
  }} } 
  public static void main(String[] args){ 
  Runnable r = new ThreadTest(); 
  Thread t1 = new Thread(r); 
  Thread t2 = new Thread(r); 
  t1.start(); 
  t2.start(); 
  }}

這個程序與示例的運行結果同樣。在可能的狀況下,應該把保護範圍縮到最小,能夠用示例的形式,this 表明「這個對象」。沒有必要把整個run()保護起來,run()中的代碼只有一個for循環,因此只要保護for 循環就能夠了。 

  public class ThreadTest implements Runnable{ 
  public void run(){ 
  for(int k=0;k<5;k++){ 
  System.out.println(Thread.currentThread().getName()+ " : for loop : " + k); 
  } 
  synchronized(this){ 
  for(int k=0;k<5;k++) { 
  System.out.println(Thread.currentThread().getName()+ " : synchronized for loop : " + k); 
  }} } 
  public static void main(String[] args){ 
  Runnable r = new ThreadTest(); 
  Thread t1 = new Thread(r,"t1_name"); 
  Thread t2 = new Thread(r,"t2_name"); 
  t1.start(); 
  t2.start(); 
  } } 
  運行結果: 
  t1_name : for loop : 0 
  t1_name : for loop : 1 
  t1_name : for loop : 2 
  t2_name : for loop : 0 
  t1_name : for loop : 3 
  t2_name : for loop : 1 
  t1_name : for loop : 4 
  t2_name : for loop : 2 
  t1_name : synchronized for loop : 0 
  t2_name : for loop : 3 
  t1_name : synchronized for loop : 1 
  t2_name : for loop : 4 
  t1_name : synchronized for loop : 2 
  t1_name : synchronized for loop : 3 
  t1_name : synchronized for loop : 4 
  t2_name : synchronized for loop : 0 
  t2_name : synchronized for loop : 1 
  t2_name : synchronized for loop : 2 
  t2_name : synchronized for loop : 3 
t2_name : synchronized for loop : 4

第一個for 循環沒有受synchronized 保護。對於第一個for 循環,t1,t2 能夠同時訪問。運行結果代表t1 執行到了k=2 時,t2 開始執行了。t1 首先執行完了第一個for 循環,此時t2尚未執行完第一個for 循環(t2 剛執行到k=2t1 開始執行第二個for 循環,當t1 的第二個for 循環執行到k=1 時,t2 的第一個for 循環執行完了。t2 想開始執行第二個for 循環,但因爲t1 首先執行了第二個for 循環,這個對象的鎖標誌天然在t1 手中(synchronized 方法的執行權也就落到了t1 手中),在t1 沒執行完第二個for 循環的時候,它是不會釋放鎖標誌的。因此t2 必須等到t1 執行完第二個for 循環後,它才能夠執行第二個for 循環。  

相關文章
相關標籤/搜索