" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java synchronized同步方法詳解

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

面試題:

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

2.一個(gè)線(xiàn)程如果出現(xiàn)了運(yùn)行時(shí)異常會(huì)怎么樣?

3.一個(gè)線(xiàn)程運(yùn)行時(shí)發(fā)生異常會(huì)怎樣?

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

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

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

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

1. synchronized 同步方法

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

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();
        // 線(xiàn)程1和線(xiàn)程2同時(shí)執(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個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)臨界區(qū)的selfIncrement()方法時(shí),就會(huì)出現(xiàn)競(jìng)態(tài)條件的問(wèn)題,即2個(gè)線(xiàn)程在臨界區(qū)代碼段的并發(fā)執(zhí)行結(jié)果因?yàn)榇a的執(zhí)行順序不同而導(dǎo)致結(jié)果無(wú)法預(yù)測(cè),每次運(yùn)行都會(huì)得到不一樣的結(jié)果。因此,為了避免競(jìng)態(tài)條件的問(wèn)題,我們必須保證臨界區(qū)代碼段操作具備排他性。這就意味著當(dāng)一個(gè)線(xiàn)程進(jìn)入臨界區(qū)代碼段執(zhí)行時(shí),其他線(xiàn)程不能進(jìn)入臨界區(qū)代碼段執(zhí)行。

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

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

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

    public int getI(){
        return i;
    }
}

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

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

2. synchronized 方法將對(duì)象作為鎖

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

public class ThreadTask {
	
    // 臨界區(qū)代碼使用synchronized關(guān)鍵字進(jìn)行保護(hù)
    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)建兩個(gè)線(xiàn)程,在兩個(gè)線(xiàn)程的執(zhí)行體中執(zhí)行線(xiàn)程邏輯:

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)建一個(gè)鎖對(duì)象,傳給兩個(gè)線(xiàn)程:

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

這里兩個(gè)線(xiàn)程的鎖對(duì)象都是threadTask,所以同一時(shí)間只有一個(gè)線(xiàn)程能拿到這個(gè)鎖對(duì)象,執(zhí)行同步代碼塊。另外,需要牢牢記住“共享”這兩個(gè)字,只有共享資源的寫(xiě)訪(fǎng)問(wèn)才需要同步化,如果不是共享資源,那么就沒(méi)有同步的必要。

總結(jié):

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

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

(3) 在Java中只有將對(duì)象作為鎖,并沒(méi)有鎖方法這種說(shuō)法;

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

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

3. 多個(gè)鎖對(duì)象

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

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ThreadTask threadTask1 = new ThreadTask();
        ThreadTask threadTask2 = new ThreadTask();
        // 兩個(gè)線(xiàn)程分別執(zhí)行兩個(gè)不同的線(xiàn)程執(zhí)行邏輯對(duì)象
        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)鍵字,任何時(shí)間只允許一個(gè)線(xiàn)程進(jìn)入同步方法,如果其他線(xiàn)程需要執(zhí)行同一個(gè)方法,那么只能等待和排隊(duì)。執(zhí)行結(jié)果呈現(xiàn)了兩個(gè)線(xiàn)程交叉輸出的效果,說(shuō)明兩個(gè)線(xiàn)程以異步方式同時(shí)運(yùn)行。

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

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

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

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

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

t1 線(xiàn)程正在運(yùn)行

此時(shí),如果我們?cè)谕椒椒ㄖ兄圃煲粋€(gè)異常:

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

在這里插入圖片描述

線(xiàn)程t1出現(xiàn)異常并釋放鎖,線(xiàn)程t2進(jìn)入方法正常輸出,說(shuō)明出現(xiàn)異常時(shí),鎖被自動(dòng)釋放了。

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

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

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

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

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

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

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

public class StaticSafe {
    // 臨界資源
    private static int count = 0;
    // 對(duì)JVM內(nèi)的所有線(xiàn)程同步
    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方法是非常粗粒度的同步機(jī)制。

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

總結(jié)

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

相關(guān)文章

  • mybatis 解決將數(shù)值0識(shí)別成空字符串的問(wèn)題

    mybatis 解決將數(shù)值0識(shí)別成空字符串的問(wèn)題

    這篇文章主要介紹了mybatis 解決將數(shù)值0識(shí)別成空字符串的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java中如何判斷中文字符串長(zhǎng)度

    Java中如何判斷中文字符串長(zhǎng)度

    這篇文章主要介紹了Java中如何判斷中文字符串長(zhǎng)度問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • JS求多個(gè)數(shù)組的重復(fù)數(shù)據(jù)

    JS求多個(gè)數(shù)組的重復(fù)數(shù)據(jù)

    這篇文章主要介紹了JS求多個(gè)數(shù)組的重復(fù)數(shù)據(jù)的辦法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • mybatis中orderBy(排序字段)和sort(排序方式)引起的bug及解決

    mybatis中orderBy(排序字段)和sort(排序方式)引起的bug及解決

    這篇文章主要介紹了mybatis中orderBy(排序字段)和sort(排序方式)引起的bug,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java中ArrayList類(lèi)的用法與源碼完全解析

    Java中ArrayList類(lèi)的用法與源碼完全解析

    這篇文章主要介紹了Java中ArrayList類(lèi)的用法與源碼完全解析,ArrayList類(lèi)通過(guò)List接口實(shí)現(xiàn),是Java中引申出的一種數(shù)據(jù)結(jié)構(gòu),需要的朋友可以參考下
    2016-05-05
  • SpringBoot瘦身打包部署的實(shí)現(xiàn)

    SpringBoot瘦身打包部署的實(shí)現(xiàn)

    這篇文章主要介紹了SpringBoot瘦身打包部署的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Spring Security靈活的PasswordEncoder加密方式解析

    Spring Security靈活的PasswordEncoder加密方式解析

    這篇文章主要介紹了Spring Security靈活的PasswordEncoder加密方式解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Java中security與shiro的區(qū)別詳解

    Java中security與shiro的區(qū)別詳解

    這篇文章主要介紹了Java中security與shiro的區(qū)別詳解,Spring?Security在架構(gòu)上將認(rèn)證與授權(quán)分離,并提供了擴(kuò)展點(diǎn),它是一個(gè)輕量級(jí)的安全框架,它確?;赟pring的應(yīng)用程序提供身份驗(yàn)證和授權(quán)支持,需要的朋友可以參考下
    2023-08-08
  • Java并發(fā)系列之AbstractQueuedSynchronizer源碼分析(概要分析)

    Java并發(fā)系列之AbstractQueuedSynchronizer源碼分析(概要分析)

    這篇文章主要為大家詳細(xì)介紹了Java并發(fā)系列之AbstractQueuedSynchronizer源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Java實(shí)現(xiàn)簡(jiǎn)單密碼加密功能

    Java實(shí)現(xiàn)簡(jiǎn)單密碼加密功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單密碼加密功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03

最新評(píng)論