" />

最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java synchronized同步方法詳解

 更新時間:2022年03月02日 11:03:53   作者:小小茶花女  
這篇文章主要為大家詳細(xì)介紹了Java synchronized同步方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

面試題:

1.如何保證多線程下 i++ 結(jié)果正確?

2.一個線程如果出現(xiàn)了運行時異常會怎么樣?

3.一個線程運行時發(fā)生異常會怎樣?

為了避免臨界區(qū)的競態(tài)條件發(fā)生,有多種手段可以達(dá)到目的。

(1) 阻塞式的解決方案:synchronized,Lock

(2) 非阻塞式的解決方案:原子變量

synchronized 即俗稱的【對象鎖】,它采用互斥的方式讓同一 時刻至多只有一個線程能持有【對象鎖】,其它線程再想獲取這個【對象鎖】時就會阻塞住。這樣就能保證擁有鎖 的線程可以安全的執(zhí)行臨界區(qū)內(nèi)的代碼,不用擔(dān)心線程上下文切換。

1. synchronized 同步方法

當(dāng)使用synchronized關(guān)鍵字修飾一個方法的時候,該方法被聲明為同步方法,關(guān)鍵字synchronized的位置處于同步方法的返回類型之前。

public class SafeDemo {
    // 臨界區(qū)資源
    private static int i = 0;

    // 臨界區(qū)代碼
    public void selfIncrement(){
        for(int j=0;j<5000;j++){
            i++;
        }
    }

    public int getI(){
        return i;
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        SafeDemo safeDemo = new SafeDemo();
        // 線程1和線程2同時執(zhí)行臨界區(qū)代碼段
        Thread t1 = new Thread(()->{
            safeDemo.selfIncrement();
        });
        Thread t2 = new Thread(()->{
            safeDemo.selfIncrement();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(safeDemo.getI()); // 9906
    }
}

可以發(fā)現(xiàn),當(dāng)2個線程同時訪問臨界區(qū)的selfIncrement()方法時,就會出現(xiàn)競態(tài)條件的問題,即2個線程在臨界區(qū)代碼段的并發(fā)執(zhí)行結(jié)果因為代碼的執(zhí)行順序不同而導(dǎo)致結(jié)果無法預(yù)測,每次運行都會得到不一樣的結(jié)果。因此,為了避免競態(tài)條件的問題,我們必須保證臨界區(qū)代碼段操作具備排他性。這就意味著當(dāng)一個線程進入臨界區(qū)代碼段執(zhí)行時,其他線程不能進入臨界區(qū)代碼段執(zhí)行。

現(xiàn)在使用synchronized關(guān)鍵字對臨界區(qū)代碼段進行保護,代碼如下:

public class SafeDemo {
    // 臨界區(qū)資源
    private static int i = 0;

    // 臨界區(qū)代碼使用synchronized關(guān)鍵字進行保護
    public synchronized void selfIncrement(){
        for(int j=0;j<5000;j++){
            i++;
        }
    }

    public int getI(){
        return i;
    }
}

經(jīng)過多次運行測試用例程序,累加10000次之后,最終的結(jié)果不再有偏差,與預(yù)期的結(jié)果(10000)是相同的。

在方法聲明中設(shè)置synchronized同步關(guān)鍵字,保證其方法的代碼執(zhí)行流程是排他性的。任何時間只允許一個線程進入同步方法(臨界區(qū)代碼段),如果其他線程需要執(zhí)行同一個方法,那么只能等待和排隊。

2. synchronized 方法將對象作為鎖

定義線程的執(zhí)行邏輯:

public class ThreadTask {
	
    // 臨界區(qū)代碼使用synchronized關(guān)鍵字進行保護
    public synchronized void test() {
        try {
            System.out.println(Thread.currentThread().getName()+" begin");
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName()+" end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

分別創(chuàng)建兩個線程,在兩個線程的執(zhí)行體中執(zhí)行線程邏輯:

public class ThreadA extends Thread {

    ThreadTask threadTask ;

    public ThreadA(ThreadTask threadTask){
        super();
        this.threadTask = threadTask;
    }

    @Override
    public void run() {
        threadTask.test();
    }
}
public class ThreadB extends Thread {
    ThreadTask threadTask ;

    public ThreadB(ThreadTask threadTask){
        super();
        this.threadTask = threadTask;
    }

    @Override
    public void run() {
        threadTask.test();
    }
}

創(chuàng)建一個鎖對象,傳給兩個線程:

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ThreadTask threadTask = new ThreadTask();
        ThreadA t1 = new ThreadA(threadTask);
        ThreadB t2 = new ThreadB(threadTask);
        t1.start();
        t2.start();
    }
}

執(zhí)行結(jié)果:

Thread-0 begin
Thread-0 end
Thread-1 begin
Thread-1 end

這里兩個線程的鎖對象都是threadTask,所以同一時間只有一個線程能拿到這個鎖對象,執(zhí)行同步代碼塊。另外,需要牢牢記住“共享”這兩個字,只有共享資源的寫訪問才需要同步化,如果不是共享資源,那么就沒有同步的必要。

總結(jié):

(1) A線程先持有object對象的鎖,B線程如果在這時調(diào)用object對象中的synchronized類型的方法,則需等待,也就是同步;

(2) 在方法聲明處添加synchronized并不是鎖方法,而是鎖當(dāng)前類的對象;

(3) 在Java中只有將對象作為鎖,并沒有鎖方法這種說法;

(4) 在Java語言中,鎖就是對象,對象可以映射成鎖,哪個線程拿到這把鎖,哪個線程就可以執(zhí)行這個對象中的synchronized同步方法;

(5) 如果在X對象中使用了synchronized關(guān)鍵字聲明非靜態(tài)方法,則X對象就被當(dāng)成鎖;

3. 多個鎖對象

創(chuàng)建兩個線程執(zhí)行邏輯ThreadTask對象,即產(chǎn)生了兩把鎖

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ThreadTask threadTask1 = new ThreadTask();
        ThreadTask threadTask2 = new ThreadTask();
        // 兩個線程分別執(zhí)行兩個不同的線程執(zhí)行邏輯對象
        ThreadA t1 = new ThreadA(threadTask1);
        ThreadB t2 = new ThreadB(threadTask2);
        t1.start();
        t2.start();
    }
}

執(zhí)行結(jié)果:

Thread-0 begin
Thread-1 begin
Thread-0 end
Thread-1 end

test()方法使用了synchronized關(guān)鍵字,任何時間只允許一個線程進入同步方法,如果其他線程需要執(zhí)行同一個方法,那么只能等待和排隊。執(zhí)行結(jié)果呈現(xiàn)了兩個線程交叉輸出的效果,說明兩個線程以異步方式同時運行。

在系統(tǒng)中產(chǎn)生了兩個鎖,ThreadA的鎖對象是threadTask1,ThreadB的鎖對象是threadTas2,線程和業(yè)務(wù)對象屬于一對一的關(guān)系,每個線程執(zhí)行自己所屬業(yè)務(wù)對象中的同步方法,不存在鎖的爭搶關(guān)系,所以運行結(jié)果是異步的。

synchronized方法的同步鎖實質(zhì)上使用了this對象鎖,哪個線程先執(zhí)行帶synchronized關(guān)鍵字的方法,哪個線程就持有該方法所屬對象作為鎖(哪個對象調(diào)用了帶有synchronized關(guān)鍵字的方法,哪個對象就是鎖),其他線程只能等待,前提是多個線程訪問的是同一個對象。

4. 如果同步方法內(nèi)的線程拋出異常會發(fā)生什么?

public class SafeDemo {
    public synchronized void selfIncrement(){
        if(Thread.currentThread().getName().equals("t1")){
            System.out.println("t1 線程正在運行");
            int a=1;
            // 死循環(huán),只要t1線程沒有執(zhí)行完這個方法,就不會釋放鎖
            while (a==1){
                
            }
        }else{
            System.out.println("t2 線程正在運行");
        }
    }
}
public class SafeDemo {
    public synchronized void selfIncrement(){
        if(Thread.currentThread().getName().equals("t1")){
            System.out.println("t1 線程正在運行");
            int a=1;
            while (a==1){
                Integer.parseInt("a");
            }
        }else{
            System.out.println("t2 線程正在運行");
        }
    }
}

執(zhí)行結(jié)果:t2線程得不到執(zhí)行

t1 線程正在運行

此時,如果我們在同步方法中制造一個異常:

public class SafeDemo {
    public synchronized void selfIncrement(){
        if(Thread.currentThread().getName().equals("t1")){
            System.out.println("t1 線程正在運行");
            int a=1;
            while (a==1){
                Integer.parseInt("a");
            }
        }else{
            System.out.println("t2 線程正在運行");
        }
    }
}

在這里插入圖片描述

線程t1出現(xiàn)異常并釋放鎖,線程t2進入方法正常輸出,說明出現(xiàn)異常時,鎖被自動釋放了。

5. 靜態(tài)的同步方法

在Java世界里一切皆對象。Java有兩種對象:Object實例對象和Class對象。每個類運行時的類型信息用Class對象表示,它包含與類名稱、繼承關(guān)系、字段、方法有關(guān)的信息。JVM將一個類加載入自己的方法區(qū)內(nèi)存時,會為其創(chuàng)建一個Class對象,對于一個類來說其Class對象是唯一的。Class類沒有公共的構(gòu)造方法,Class對象是在類加載的時候由Java虛擬機調(diào)用類加載器中的defineClass方法自動構(gòu)造的,因此不能顯式地聲明一個Class對象。

普通的synchronized實例方法,其同步鎖是當(dāng)前對象this的監(jiān)視鎖。如果某個synchronized方法是static(靜態(tài))方法,而不是普通的對象實例方法,其同步鎖又是什么呢?

public class StaticSafe {
    // 臨界資源
    private static int count = 0;
    // 使用synchronized關(guān)鍵字修飾static方法
    public static synchronized void test(){
        count++;
    }
}

靜態(tài)方法屬于Class實例而不是單個Object實例,在靜態(tài)方法內(nèi)部是不可以訪問Object實例的this引用的。所以,修飾static方法的synchronized關(guān)鍵字就沒有辦法獲得Object實例的this對象的監(jiān)視鎖。

實際上,使用synchronized關(guān)鍵字修飾static方法時,synchronized的同步鎖并不是普通Object對象的監(jiān)視鎖,而是類所對應(yīng)的Class對象的監(jiān)視鎖。

為了以示區(qū)分,這里將Object對象的監(jiān)視鎖叫作對象鎖,將Class對象的監(jiān)視鎖叫作類鎖。當(dāng)synchronized關(guān)鍵字修飾static方法時,同步鎖為類鎖;當(dāng)synchronized關(guān)鍵字修飾普通的成員方法時,同步鎖為對象鎖。由于類的對象實例可以有很多,但是每個類只有一個Class實例,因此使用類鎖作為synchronized的同步鎖時會造成同一個JVM內(nèi)的所有線程只能互斥地進入臨界區(qū)段。

public class StaticSafe {
    // 臨界資源
    private static int count = 0;
    // 對JVM內(nèi)的所有線程同步
    public static synchronized void test(){
        count++;
    }
}
z'z'z'z'z'z'z'z'z'z'z'z'z'z'z'z'z'z'z

所以,使用synchronized關(guān)鍵字修飾static方法是非常粗粒度的同步機制。

通過synchronized關(guān)鍵字所搶占的同步鎖什么時候釋放呢?一種場景是synchronized塊(代碼塊或者方法)正確執(zhí)行完畢,監(jiān)視鎖自動釋放;另一種場景是程序出現(xiàn)異常,非正常退出synchronized塊,監(jiān)視鎖也會自動釋放。所以,使用synchronized塊時不必?fù)?dān)心監(jiān)視鎖的釋放問題。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!  

相關(guān)文章

  • Spring 應(yīng)用上下文獲取 Bean 的常用姿勢實例總結(jié)

    Spring 應(yīng)用上下文獲取 Bean 的常用姿勢實例總結(jié)

    這篇文章主要介紹了Spring 應(yīng)用上下文獲取 Bean,結(jié)合實例形式總結(jié)分析了Spring 應(yīng)用上下文獲取 Bean的實現(xiàn)方法與操作注意事項,需要的朋友可以參考下
    2020-05-05
  • 詳解SpringCloudGateway內(nèi)存泄漏問題

    詳解SpringCloudGateway內(nèi)存泄漏問題

    這篇文章主要介紹了詳解SpringCloudGateway內(nèi)存泄漏問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 如何通過Java打印Word文檔

    如何通過Java打印Word文檔

    這篇文章主要介紹了如何通過Java打印Word文檔,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • Mybatis框架搭建與簡單查詢詳解

    Mybatis框架搭建與簡單查詢詳解

    本文主要介紹了Mybatis框架搭建與簡單查詢的相關(guān)知識,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • SpringBoot將項目打成war包步驟解析

    SpringBoot將項目打成war包步驟解析

    這篇文章主要介紹了SpringBoot將項目打成war包步驟解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • Ireport的安裝與使用教程

    Ireport的安裝與使用教程

    這篇文章主要介紹了Ireport的安裝與使用教程,需要的朋友可以參考下
    2021-10-10
  • Spring Cloud  Hystrix實現(xiàn)服務(wù)容錯的方法

    Spring Cloud  Hystrix實現(xiàn)服務(wù)容錯的方法

    Hystrix是SpringCloud中重要的熔斷保護組件,由Netflix開源,主要提供延遲和容錯管理,以保障分布式系統(tǒng)的高可用性和魯棒性,通過封裝依賴項實現(xiàn)服務(wù)間隔離,引入回退邏輯應(yīng)對依賴服務(wù)故障,有效防止系統(tǒng)崩潰和服務(wù)級聯(lián)故障
    2024-10-10
  • java8 BigDecimal類型的List求和方式

    java8 BigDecimal類型的List求和方式

    這篇文章主要介紹了java8 BigDecimal類型的List求和方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-11-11
  • Mybatis詳解動態(tài)SQL以及單表多表查詢的應(yīng)用

    Mybatis詳解動態(tài)SQL以及單表多表查詢的應(yīng)用

    MyBatis的動態(tài)SQL是基于OGNL表達(dá)式的,它可以幫助我們方便的在SQL語句中實現(xiàn)某些邏輯,下面這篇文章主要給大家介紹了關(guān)于Mybatis超級強大的動態(tài)SQL語句的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • Spring Boot使用Value注解給靜態(tài)變量賦值的方法

    Spring Boot使用Value注解給靜態(tài)變量賦值的方法

    這篇文章主要介紹了Spring Boot使用Value注解給靜態(tài)變量賦值的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07

最新評論

靖西县| 阳曲县| 高雄市| 会昌县| 阿坝县| 北海市| 叶城县| 扶沟县| 乌兰察布市| 西昌市| 连山| 宜良县| 青龙| 罗江县| 宕昌县| 衢州市| 通海县| 邹城市| 灵石县| 广州市| 铜川市| 海宁市| 中方县| 临沧市| 普定县| 岐山县| 鲁甸县| 云阳县| 兰州市| 满洲里市| 宁陵县| 盐边县| 漳浦县| 铁岭县| 琼中| 兴城市| 神木县| 仪征市| 彭山县| 咸宁市| 衢州市|