Java併發之synchronized使用

synchronized,是Java語言的關鍵字,讀['siŋkrənaizd],當它用來修飾一個方法或者一個代碼塊的時候,可以保證在同一時刻最多隻有一個線程執行該段代碼。編程

1、Java爲什麼要使用synchronized?

線程的同步是爲了防止多個線程訪問一個數據對象時,對數據形成的破壞。爲確保共享變量不會出現併發問題,一般會對修改共享變量的代碼塊用synchronized加鎖,確保同一時刻只有一個線程在修改共享變量,從而避免併發問題。因此須要緊緊記住「共享」這兩個字,只有共享資源的讀寫訪問才須要同步化,若是不是共享資源,那麼就沒有同步的必要。安全

2、synchronized修飾範圍

  1. 修飾實例方法 > 多個線程訪問同一個實例的加鎖方法時,會出現鎖的競爭多線程

  2. 修飾靜態方法 > 多個線程訪問類的加鎖方法時,會出現鎖的競爭併發

  3. 修飾代碼塊 > 多線程訪問到同一個代碼塊時,會出現競爭的問題ide

3、synchronized同步鎖對象

synchronized能夠用來修飾方法或代碼塊,咱們能夠把獲取的同步鎖歸爲如下3種:學習

  1. 實例對象鎖:修飾在普通方法上(非靜態方法);在代碼塊中修飾this即synchronized(this)代碼塊
  2. 類對象鎖:修飾在靜態方法上;在代碼塊中修飾class即synchronized(X.class)代碼塊
  3. 同步塊非當前實例對象鎖:在代碼塊中修飾非當前實例對象,好比在X類中synchronized(對象a)的代碼

對這3種不一樣的鎖,使用相互之間不受影響,對於同一種鎖,會出現鎖的競態條件。測試

4、synchronized同步鎖使用

對於1.實例對象鎖的使用:this

  • 全部的非靜態同步方法用的都是同一把鎖—實例對象自己,也就是說若是一個實例對象的非靜態同步方法獲取鎖後,該實例對象的其餘非靜態同步方法必須等待獲取鎖的方法釋放鎖後才能獲取鎖。
  • 實例對象鎖存在於當前實例,不一樣的實例對象的鎖相互之間不受影響。
  • synchronized(this)代碼塊獲取的是該實例對應的鎖,與非靜態的synchronized同步方法使用的是同一把鎖,會出現鎖的競爭。
  • 實例鎖和類對象鎖沒有影響,不會形成彼此阻塞。

對於2.類對象鎖的使用:spa

  • 對於靜態同步方法,鎖是針對這個類的,鎖對象是該類的Class對象。
  • 而全部的靜態同步方法用的也是同一把鎖——類對象自己,若是一個靜態同步方法獲取鎖後,其餘的靜態同步方法都必須等待該方法釋放鎖後才能獲取鎖。
  • 不論是同一個實例對象的靜態同步方法之間,仍是不一樣的實例對象的靜態同步方法之間,他們都使用的是同一個類的對象鎖,會出現鎖的競爭。
  • synchronized(X.class)代碼塊使用的是類Class的對象鎖,與類中靜態同步方法會出現鎖的競爭。多個類中的synchronized(X.class)代碼塊也會出現鎖的競態條件。

對於3.同步塊非當前實例對象鎖的使用:.net

  • 對於同步塊,因爲其對象鎖是能夠選擇的,只有使用同一把鎖的同步塊之間纔有着競態條件。
  • 同步鎖的對象是基於實際對象而不是對象引用的,因此使用時特別注意,在鎖的做用域中因改變實際對象引用從而引發鎖的對象改變致使同步鎖失去競太條件。

5、synchronized使用小結

關於鎖和同步的使用,彙總如下幾個要點:
一、同步鎖只能經過同步方法去保證共享變量安全,而不是同步變量和類。
二、當提到同步時,應該清楚在什麼上同步?也就是說,在哪一個對象上同步?
三、沒必要同步類中全部的方法,類能夠同時擁有同步和非同步方法。
四、對於出現競態條件鎖的線程,一個線程獲取了鎖,其餘線程會阻塞等待該鎖的釋放去獲取該鎖。
五、若是線程擁有同步和非同步方法,則非同步方法能夠被多個線程自由訪問而不受鎖的限制。
六、線程睡眠時,它所持的任何鎖都不會釋放。
七、線程能夠得到多個重進入(synchronized )鎖。好比,在一個對象的同步方法裏面調用另一個對象的同步方法,則獲取了兩個對象的同步鎖。
八、同步損害併發性,應該儘量縮小同步範圍。同步不但能夠同步整個方法,還能夠同步方法中一部分代碼塊。
九、在使用同步代碼塊時候,應該指定在哪一個對象上同步,也就是說要獲取哪一個對象的鎖
十、編寫線程安全的類,須要時刻注意對多個線程競爭訪問資源的邏輯和安全作出正確的判斷,對「原子」操做作出分析,並保證原子操做期間別的線程沒法訪問。
 

3.測試case代碼

package com.test.synchroniz;

public class Service {
    
    public Service(){
        System.out.println("當前線程:" + Thread.currentThread().getName() + " 構造方法");
    }
    
    static{
        System.out.println("當前線程:" + Thread.currentThread().getName() + " 靜態代碼塊");
    }
    
    private Object object1 = new Object();
    private Object object2 = new Object();
    
    synchronized public static void printA(){
        try{
            System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法A");
            Thread.sleep(3000);
            System.out.println("當前線程:" + Thread.currentThread().getName()  + Thread.currentThread().getId()  +"在 " + System.currentTimeMillis() + " 退出方法A");
        }catch(Exception e){
            System.out.println(e);
        }
    }
    
    synchronized public void printB(){
        try{
            System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法B");
            Thread.sleep(3000);
            System.out.println("當前線程:" + Thread.currentThread().getName()  + Thread.currentThread().getId()  +"在 " + System.currentTimeMillis() + " 退出方法B");
        }catch(Exception e){
            System.out.println(e);
        }
    }
    
     public void printC(){
         System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法C");
        try{
            synchronized(object1){
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + "進入方法C--synchronized{X}");
                Thread.sleep(3000);
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法C-synchronized{X}");
            }
            System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法C");
            }catch(Exception e){
            System.out.println(e);
        }
    }

    
     public void printD(){
         System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法D");
        try{
            synchronized(object2){
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + "進入方法D--synchronized{X}");
                Thread.sleep(3000);
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法D-synchronized{X}");
            }
            System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法D");
            }catch(Exception e){
            System.out.println(e);
        }
    }
     
     public void printE(){
         System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法E");
        try{
            synchronized(this){
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法E--synchronized{this}");
                Thread.sleep(3000);
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法E-synchronized{this}");
            }
            System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法E");
            }catch(Exception e){
            System.out.println(e);
        }
    }
     
     public static void printF(){
         System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法E");
        try{
            synchronized(Service.class){
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法F--synchronized{class}");
                Thread.sleep(3000);
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法F-synchronized{class}");
            }
            System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法F");
            }catch(Exception e){
            System.out.println(e);
        }
    }
     
    public void printG(){
         System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法G");
        try{
            synchronized(Service.class){
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 進入方法G--synchronized{class}");
                Thread.sleep(3000);
                System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法G-synchronized{class}");
            }
            System.out.println("當前線程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法G");
            }catch(Exception e){
            System.out.println(e);
        }
    }



}
View Code
package com.test.synchroniz;

/**
 * 0.synchronized同步方法、synchronized靜態同步方法分別是用到的是實例鎖,類鎖,一個線程獲取到synchronized同步方法的鎖時,
 * 另外一線程依然能夠進入synchronized靜態同步方法(實例鎖,類鎖二者不一樣,相互不影響
 * )
 * 1.synchronized同步方法,synchronized(this)都是對象鎖,對於其餘線程調用synchronized同步方法,synchronized(this)呈阻塞狀態 </br>
 * 2.同一時間同一線程只有一個線程獲取對象鎖執行 </br>
 * 
 * 1.synchronized(非this)對象鎖,對於非this若是是同一對象,兩個線程同時只有一個能夠獲取該鎖 </br>
 * 2.對象鎖(synchronized同步方法 或 synchronized(this))、synchronized(非this)對象鎖 兩個線程同時執行,均可得到各自的鎖 </br>
 *
 * 1.synchronized修飾static方法與synchronized(X.class)做用同樣
 * 
 * @author fugaoyang
 *
 */
public class TestRun {

    public static void main(String[] args) throws Exception {
        Service service = new Service();
        Thread threadA = new Thread("A"){
            @Override
            public void run(){
                service.printA();
            }
        };
        
        Thread threadB = new Thread("B"){
            @Override
            public void run(){
                service.printB();
            }
        };
        
        Thread threadC = new Thread("C"){
            @Override
            public void run(){
                service.printC();
            }
        };
        
        Thread threadD = new Thread("D"){
            @Override
            public void run(){
                service.printD();
            }
        };
        
        Thread threadE = new Thread("E"){
            @Override
            public void run(){
                service.printE();
            }
        };
        
        Thread threadF = new Thread("F"){
            @Override
            public void run(){
                service.printF();
            }
        };
        
        Thread threadG = new Thread("G"){
            @Override
            public void run(){
                service.printG();
            }
        };
        
        threadA.start();
        //threadB.start();
        //threadC.start();
        //threadD.start();
        //threadE.start();
        threadF.start();
        threadG.start();
        
        threadA.join();
        threadF.join();
        threadG.join();
    }
}
View Code
 
本篇文章主要總結了synchronized同步鎖的使用,同步鎖的原理和機制後續會作深刻了解和學習。
 
本文參考部分出處:
3.《Java多線程編程核心技術-高洪嚴》
相關文章
相關標籤/搜索