Synchronized的作用


Synchronized

官方解釋:

  同步方法支持一種簡單的策略來防止線程干擾和內存一致性錯誤:如果一個對象對多個線程可見,則對該對象變量的所有讀取或寫入都是通過同步方法完成的。

一句話總結出Synchronized的作用:

  能夠保證在同一時刻最多只有一個線程執行該段代碼,以達到保證並發安全的效果

Synchronized的地位:

  synchronized是Java的關鍵字,被Java語言原生支持

  是最基本的互斥同步手段

  必學

synchronized的兩個用法

對象鎖:

  包含方法鎖(默認鎖對象為this當前實力對象),同步代碼塊鎖(自己制定鎖對象)

類鎖:

  指sychronized修飾靜態的方法或指鎖為Class對象

第一個用法:對象鎖

  代碼塊形式:手動指定鎖對象

  方法鎖形式:synchronized修飾普通方法,鎖默認對象為this

 

第二個用法:類鎖

  概念:java類可能有有很多個對象,但是只有一個class對象

  本質:所以所謂的類鎖,不過是Class對象的鎖而已

  用法和效果:類鎖只能在同一時刻被一個對象擁有

  形式1:synchronized加載static方法上

  形式2:synchronized(*.class)代碼塊

多線程訪問同步方法的7種情況

1.兩個線程同時訪問一個對象的同步方法

  串行

2.兩個線程訪問的是兩個對象的同步方法

  鎖對象不同,互不干擾,並行

3.如果兩個線程訪問的是Synchronized的靜態方法

  串行

4.同時訪問同步方法與非同步方法

  並行

5.訪問同一對象的不同的普通同步方法

  同一對象鎖,串行

6.同時訪問靜態synchronized和非靜態synchronized方法

  鎖不同,並行

7.方法拋異常后,會釋放鎖嗎

  如果一個線程在進入同步方法后拋出了異常,則另一個線程會立刻進入該同步方法

7種情況的總結

1.一把鎖只能同時被一個線程獲取,沒有拿到鎖的線程必須等待(對應1,5種情況)

2.每個實例都對應有自己的一把鎖,不同實例之間互不影響,例如,鎖對象是*.class以及synchronized修飾的

是static方法的時候,所有對象共用同一把鎖(對應第2,3,4,6種情況)

3.無論是方法正常執行完步或者方法拋出異常,都會釋放鎖(對應第7種情況)

性質

1.可重入

什么是可重入(遞歸鎖)-----例如:Synchronized,ReentranLock

  指的是同一線程的外層方法獲得獲得鎖之后,內層方法可以直接在此獲取該鎖

    例如: 買完車,需要搖號上車牌(鎖),這樣才能開車(執行相應的方法).

    如果我有三輛車,但只有一個車牌,顯然只能給一輛車上車牌----獲取鎖后只能執行一個方法

                                      ---------------為不可重入性

    反之:一個車牌,三輛車都可以開

                                 ---------------為可重入性

好處:

  避免死鎖,提高封裝性

粒度(范圍):

  可重入粒度測試: 證明線程而非調用(用三種情況來說明和pthread的區別)

    情況一:證明同一方法是可重入的---遞歸調用本方法

package cn.cg;
/**
 * 可重入測試----使用遞歸方式   
 * 情況1:同一個類中,同一方法可重入
 */
public class SynchronizedTest {
    int i = 0;
    //主線程可以重入以this為鎖對象的method方法
    public static void main(String[] args) {
        SynchronizedTest s1 = new  SynchronizedTest();
        s1.method();
    }
    // 同步方法
    private synchronized void method() {
        if (i <=3) {
            i++;
            System.out.println(i);
            method();
        }
    }
}

 

 

 測試通過.

    情況二:證明可重入不要求是同一個方法

 

package cn.cg;
/**
 * 可重入測試----   
 * 情況2:同一個類中,不同方法可重入
 */
public class SynchronizedTest2 {
    //主線程可以重入以this為鎖對象的method方法
    public static void main(String[] args) {
        SynchronizedTest2 s1 = new  SynchronizedTest2();
        s1.method1();
    }
    // 同步方法
    private synchronized void method1() {
        System.out.println("method1");
            method2();
        
    }
    private synchronized void method2() {
        System.out.println("method2");
    }
}

 

 

 測試通過

    情況三:證明可重入不要求是同一個類中的

public class Demo1 {
    public synchronized void method(){
        System.out.println("我是Demo1");
    }

}
class Demo1Zi extends  Demo1{
    public synchronized void method(){
        System.out.println("我是Demo1兒子");
        super.method();
    }

    public static void main(String[] args) {
        Demo1Zi zi = new Demo1Zi();
        zi.method();
    }
}

測試通過

2.不可中斷

  一旦這個鎖已經被別人獲得了,如果我還想獲取,我只能選擇等待或者阻塞,直到別的線程釋放這個鎖.如果別人永遠不釋放鎖,那么我只能永遠地等下去

  相比之下,Lock類,可以擁有中斷的能力,

    第一點:如果我覺得我等的時間太長了,有權中斷現在已經獲取到鎖的線程的執行,

    第二點:如果我絕得我等待的時間太長不想再等了,也可以退出.

 加鎖和釋放鎖的原理

  現象

  每一個類的實例對應一把鎖,每次調用被synchronized修飾的方法都必須首先獲得該方法所屬調用者實例的鎖.否則線程阻塞,

  方法一旦執行,該線程就獨占了該鎖知道該方法返回或者拋出異常,才能釋放鎖.

  獲取和釋放鎖的時機:內置鎖(監視器鎖)

  等價代碼

  

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo2 {
    private Lock lock = new ReentrantLock();
    public  synchronized  void method1(){
        System.out.println("我是synchronized鎖住的方法");
    }
    public  void method2(){
        //加鎖
        lock.lock();
        try {
            System.out.println("我是ReentranLock鎖住的方法");
        }finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        Demo2 demo2 = new Demo2();
        demo2.method1();
        demo2.method2();
    }
}

 

synchronized的缺點

  1.效率低:鎖的釋放情況少,試圖獲得鎖時不能設定超時,不能中斷一個正在試圖獲得所得線程

  2.不夠靈活(讀寫鎖更靈活):加鎖和釋放的時機單一,每個鎖僅有單一的條件(某個對象),可能是不夠的

  3.無法知道是否成功獲取到鎖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM