java多線程(四)synchronized關鍵字修飾方法

轉載請註明出處:http://blog.csdn.net/xingjiarong/article/details/47907237


在之前的博客中我們介紹了條件對象和鎖對象,兩者結合使用才能起到比較好的互斥與同步效果,大家可能覺得有些麻煩,有沒有將兩者結合起來的工具呢,有!java提供了synchronized關鍵字來實現線程的互斥和同步,其達到的效果相當於條件對象和鎖對象結合起來的效果。synchronized關鍵字有兩類用法,一類是修飾方法,一類是修飾代碼塊,這篇博客主要介紹一下synchronized關鍵字修飾方法時的用法。


先來爲大家介紹一下java中鎖的概念。java中的每個對象和每個類都有鎖,而且是互斥鎖,也就是說只能有一方佔有,另一方只能等到對方釋放後才能再佔有鎖。synchronized關鍵字就是基於java的對象和類的鎖的。


一、修飾普通方法


下面來看一下下面這個例子,Trans這個類是在一行中打印當前的線程和0-24這25個數。MyThread這個類接收一個Trans類的對象,在run方法中不停的調用printNum方法。在main方法中,創建了一個對象和兩個線程,這兩個線程都使用同一個Trans對象。

[java]  view plain  copy
  1. public class Trans {  
  2.   
  3.     public void printNum(int num){  
  4.         System.out.print(Thread.currentThread());//獲取當前運行這個方法的類  
  5.         for(int i=0;i<25;i++){  
  6.             System.out.print(i+" ");  
  7.         }  
  8.         System.out.println();  
  9.     }  
  10. }  

[java]  view plain  copy
  1. class MyThread implements Runnable {  
  2.     private Trans trans;  
  3.     private int num;  
  4.   
  5.     public MyThread(Trans trans, int num) {  
  6.         this.trans = trans;  
  7.         this.num = num;  
  8.     }  
  9.   
  10.     public void run() {  
  11.         while (true)  
  12.         {  
  13.             trans.printNum(num);  
  14.             try {  
  15.                 Thread.sleep(500);  
  16.             } catch (InterruptedException e) {  
  17.                 e.printStackTrace();  
  18.             }  
  19.         }  
  20.               
  21.     }  
  22. }  
  23.   
  24. public class Test {  
  25.   
  26.   
  27.     public static void main(String[] args) {  
  28.   
  29.         Trans t = new Trans();  
  30.         Thread a = new Thread(new MyThread(t, 1));  
  31.         Thread b = new Thread(new MyThread(t, 2));  
  32.   
  33.         a.start();  
  34.         b.start();  
  35.   
  36.     }  
  37.   
  38. }  

現在,兩個線程會同時執行Trans的printNum方法,因爲又是同一個對象的方法,所以一定會產生RaceCondition,我的結果如下:



可以看到,打印結果確實是不正確的。那麼怎麼用synchronized關鍵字避免這種情況呢?可以將printNum方法的聲明改爲下面這種:

[java]  view plain  copy
  1. public synchronized void printNum(int num){  
  2.         System.out.print(Thread.currentThread());  
  3.         for(int i=0;i<25;i++){  
  4.             System.out.print(i+" ");  
  5.         }  
  6.         System.out.println();  
  7.     }  

這就是用synchronized關鍵字修飾方法,現在我們再來試一下程序,看看還會不會產生RaceCondition。果然不會再產生RaceCondition了,是不是比用條件鎖方便多了,那麼爲什麼這樣就可以了呢?我們來分析一下:之前我們說過,synchronized關鍵字會獲得鎖,所以在這裏synchronized關鍵字獲得的鎖就是傳遞給線程的那個Trans對象的鎖,兩個線程用的是同一個鎖,所以當一個線程執行到synchronized修飾的方法時,這個線程就會獲得Trans對象的鎖,並且這個鎖是互斥的,所以其他試圖想要獲得鎖的線程都必須等待,直到這個線程釋放了鎖,從而起到了互斥的作用。


每個對象都有自己的鎖,所以這裏synchronized關鍵字起作用的原因就是因爲兩個線程用的是同一個對象,如果每個線程都有自己的Trans對象,那麼上邊的方法將不再適用,例如我們把Main方法改爲下面這樣:


[java]  view plain  copy
  1. public static void main(String[] args) {  
  2.   
  3.         Trans t = new Trans();  
  4.         Trans t1 = new Trans();  
  5.         Thread a = new Thread(new MyThread(t, 1));  
  6.         Thread b = new Thread(new MyThread(t1, 2));  
  7.   
  8.         a.start();  
  9.         b.start();  
  10.   
  11.     }  

這裏每個線程都有自己的Trans對象,每個線程獲得的鎖也是自己對象的鎖,兩個鎖之間互不干擾,所以synchronized關鍵字不會起到作用,那麼這時應該怎麼用synchronized關鍵字呢?那就是使用synchronized關鍵字修飾代碼塊,這種用法我們下篇博客再講,這篇先把synchronized關鍵字修飾方法的用法講清楚。


既然每個對象都只有一把互斥鎖,那麼同一個對象的多個synchronized關鍵字修飾的方法之間也是無法同時訪問的。例如下面的例子:


[java]  view plain  copy
  1. public class Trans {  
  2.   
  3.     public synchronized void printNum(int num) {  
  4.         while (true) {  
  5.             System.out.print(Thread.currentThread());  
  6.             for (int i = 0; i < 25; i++) {  
  7.                 System.out.print(i + " ");  
  8.             }  
  9.             System.out.println();  
  10.             try {  
  11.                 Thread.sleep(500);  
  12.             } catch (InterruptedException e) {  
  13.                 e.printStackTrace();  
  14.             }  
  15.               
  16.         }  
  17.     }  
  18.   
  19.     public synchronized void printNum1(int num) {  
  20.         System.out.print(Thread.currentThread());  
  21.         for (int i = 25; i > 0; i--) {  
  22.             System.out.print(i + " ");  
  23.         }  
  24.         System.out.println();  
  25.     }  
  26.   
  27.     public void printNum2(int num) {  
  28.         for (int i = 25; i > 0; i--) {  
  29.             System.out.print(num);  
  30.         }  
  31.         System.out.println();  
  32.     }  
  33. }  



  1. class MyThread implements Runnable {  
  2.     private Trans trans;  
  3.     private int num;  
  4.   
  5.     public MyThread(Trans trans, int num) {  
  6.         this.trans = trans;  
  7.         this.num = num;  
  8.     }  
  9.        }  
  10. }  


[java]  view plain  copy
  1. class MyThread implements Runnable {  
  2.     private Trans trans;  
  3.     private int num;  
  4.   
  5.     public MyThread(Trans trans, int num) {  
  6.         this.trans = trans;  
  7.         this.num = num;  
  8.     }  
  9.   
  10.   &:16px;height:16px;">
  1. class MyThread 
相關文章
相關標籤/搜索