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

三道java新手入門面試題,通往自由的道路--鎖+Volatile

 更新時(shí)間:2021年07月01日 10:08:10   作者:太子爺哪吒  
這篇文章主要為大家分享了最有價(jià)值的3道多線程面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,對(duì)hashCode方法的設(shè)計(jì)、垃圾收集的堆和代進(jìn)行剖析,感興趣的小伙伴們可以參考一下

1. 你知道volatile是如何保證可見(jiàn)性嗎?

我們先看一組代碼:

public class VolatileVisibleDemo {
    public static boolean initFlag = false;
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("等待initFlag改變!??!");
                // 如果initFlag發(fā)生改變了,這是為true的話,才會(huì)結(jié)束循環(huán)
                while(!initFlag) {
                }
                System.out.println("今天的世界打烊了,晚安!");
            }
        }).start();
        // 這里是為了能保證運(yùn)行完上面的代碼
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 這里是Lambda表達(dá)式,就是上面的縮寫
        new Thread(() -> {
            System.out.println("準(zhǔn)備填充數(shù)據(jù),修改initFlag的值");
            initFlag = true;
            System.out.println("準(zhǔn)備數(shù)據(jù)完了!");
        }).start();
    }
}

運(yùn)行得到的結(jié)果

我們可以發(fā)現(xiàn),其實(shí)在準(zhǔn)備數(shù)據(jù)完后,我們的initFlag的變量其實(shí)已經(jīng)改變,但是為什么還是沒(méi)有結(jié)束循環(huán)輸出**今天的世界打烊了,晚安!**這一句呢?

從之間的JMM模型,我們可以知道,不同線程之間是不能直接訪問(wèn)對(duì)方工作內(nèi)存中的變量,線程間變量的值的傳遞需要通過(guò)主內(nèi)存中轉(zhuǎn)來(lái)完成,并且線程在修改完數(shù)值后,也不是馬上同步到主內(nèi)存中,并且另一個(gè)線程也是無(wú)法感知到數(shù)據(jù)發(fā)生改變的,所以就會(huì)有可見(jiàn)性問(wèn)題。

那我們可以加個(gè)volatile關(guān)鍵字修飾變量試下?

 public static volatile boolean initFlag = false;

我們可以發(fā)現(xiàn)

在我們的變量修飾了volatile關(guān)鍵字后,就能輸出**今天的世界打烊了,晚安!**這一句了。

我們來(lái)看看圖解吧

先解釋下這其中連接的幾個(gè)單詞:

  • read(讀?。簭闹鲀?nèi)存中讀取數(shù)據(jù)
  • load (載入):將主內(nèi)存中讀取到的數(shù)據(jù)寫入到本地(工作)內(nèi)存中
  • user(使用):從本地內(nèi)存中讀取數(shù)據(jù)給線程使用來(lái)計(jì)算
  • assign(賦值):線程將計(jì)算好的值重新賦值到工作內(nèi)存中
  • store(存儲(chǔ)):將本地內(nèi)存的數(shù)據(jù)存儲(chǔ)到主內(nèi)存中
  • write(寫入):將stroe過(guò)來(lái)的變量值賦值給主內(nèi)存中的變量,重新賦值。

大概講一下流程:

在線程B讀取initFlag變量后,重新賦值true給變量,此時(shí),因?yàn)榧恿藇olatile修飾,所以會(huì)馬上將值寫入到主內(nèi)存中修改變量中的值,此時(shí)因?yàn)橛幸粋€(gè)cpu總線嗅探機(jī)制會(huì)監(jiān)聽(tīng)到主內(nèi)存的變量值發(fā)生改變了,會(huì)把本地內(nèi)存的中initFlag變量設(shè)置了失效,重新讀取一邊主內(nèi)存的新值,就可以達(dá)到解決變量可見(jiàn)性問(wèn)題。這是它第一個(gè)保證可見(jiàn)性的關(guān)鍵。

之前我們也有提到他如果發(fā)生指令重排序了,那是不是也不能讀取到最新的值呢。答案是不會(huì)的呢。

因?yàn)楸籿olatile修飾的話,它會(huì)禁止指令重排序。那它主要是依靠什么指令重排序呢?它是通過(guò)內(nèi)存屏障來(lái)實(shí)現(xiàn)的。什么是內(nèi)存屏障?硬件層面,內(nèi)存屏障分兩種:讀屏障(Load Barrier)和寫屏障(Store Barrier)。內(nèi)存屏障有兩個(gè)作用:

  1. 阻止屏障兩側(cè)的指令重排序;
  2. 強(qiáng)制把寫緩沖區(qū)/高速緩存中的臟數(shù)據(jù)等寫回主內(nèi)存,或者讓緩存中相應(yīng)的數(shù)據(jù)失效。

而編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來(lái)禁止指令重排序。這樣保證了任何程序中都能得到正確的volatile內(nèi)存語(yǔ)義。這個(gè)策略是:

  • 在每個(gè)volatile寫操作前插入一個(gè)StoreStore屏障;
  • 在每個(gè)volatile寫操作后插入一個(gè)StoreLoad屏障;
  • 在每個(gè)volatile讀操作后插入一個(gè)LoadLoad屏障;
  • 在每個(gè)volatile讀操作后再插入一個(gè)LoadStore屏障。

看一下示意圖

小結(jié):

volatile作用

  1. volatile可以保證內(nèi)存可見(jiàn)性且禁止重排序。
  2. volatile不具備保證原子性,而鎖可以保證整個(gè)臨界區(qū)代碼的執(zhí)行具有原子性。所以而鎖可以保證整個(gè)臨界區(qū)代碼的執(zhí)行具有原子性。所以在功能上,鎖比volatile更強(qiáng)大;在性能上,volatile更有優(yōu)勢(shì)。

2. 悲觀鎖和樂(lè)觀鎖可以講下你的理解嗎?

其實(shí)聽(tīng)名字,我們就應(yīng)該有個(gè)概念:

悲觀對(duì)應(yīng)著我們生活中的人,悲觀的人一般看待事物都會(huì)相對(duì)消極負(fù)能量點(diǎn),會(huì)盡可能往壞處去想的。這也是對(duì)應(yīng)著MyGirl,她其實(shí)是一個(gè)也不能說(shuō)算是悲觀的人,只能說(shuō)看待事物可能會(huì)更往深入,更壞的一方面的去思考。

這其實(shí)跟我很互補(bǔ),因?yàn)樗闶莻€(gè)樂(lè)天派吧,而樂(lè)觀對(duì)應(yīng)著我們生活中的人,樂(lè)觀的人一般看待事物都會(huì)相對(duì)積極正能量,會(huì)盡可能往好處去想的。我其實(shí)對(duì)待生活的方方面面可能會(huì)更樂(lè)觀點(diǎn),但有時(shí)帶來(lái)的一些壞處也是難以估計(jì)的。

所以說(shuō)這兩者不能說(shuō)誰(shuí)好誰(shuí)壞,只能對(duì)應(yīng)著場(chǎng)景選擇對(duì)應(yīng)的方法。

悲觀鎖:

MyGilr這個(gè)人呢,她總是會(huì)假設(shè)一種最壞的情況。比如,她每次要去拿數(shù)據(jù)的同時(shí),認(rèn)為別人也會(huì)來(lái)修改數(shù)據(jù)跟她作對(duì),所以每次在拿數(shù)據(jù)的時(shí)候她都會(huì)上鎖,堵上一個(gè)界限,這樣別人想拿這個(gè)數(shù)據(jù)就只能等待她出去解鎖成功后,直到它拿到鎖。

在Java中,synchronizedReentrantLock等獨(dú)占鎖就是悲觀鎖思想的實(shí)現(xiàn)。而在數(shù)據(jù)庫(kù)里邊就用到了很多這種鎖機(jī)制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。

樂(lè)觀鎖:

我這個(gè)人呢,總是會(huì)假設(shè)一種最好的情況。比如, 我每次要去拿數(shù)據(jù)的同時(shí),認(rèn)為別人絕對(duì)不會(huì)來(lái)修改數(shù)據(jù)滴,所以每次拿數(shù)據(jù)的時(shí)候都不會(huì)上鎖。但是人還是要點(diǎn)防備心里的,不是嗎?所以在更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新過(guò)這個(gè)數(shù)據(jù)。

而常見(jiàn)的有CAS算法+版本號(hào)實(shí)現(xiàn)。樂(lè)觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量。

在Java中,像原子類就是使用了樂(lè)觀鎖的一種實(shí)現(xiàn)方式CAS實(shí)現(xiàn)的。而在數(shù)據(jù)庫(kù)提供的類似于write_condition機(jī)制,其實(shí)都是提供的樂(lè)觀鎖。

兩者對(duì)應(yīng)的場(chǎng)景的區(qū)別:

樂(lè)觀鎖多用于讀多寫少的環(huán)境,避免頻繁加鎖影響性能,加大了系統(tǒng)的整個(gè)吞吐量;而悲觀鎖多用于寫多讀少的環(huán)境,避免頻繁失敗和重試影響性能。

3. 你還知道什么其他的鎖嗎?

可重入鎖和非可重入鎖:

所謂重入鎖又名遞歸鎖,顧名思義。就是支持重新進(jìn)入的鎖,也就是說(shuō)這個(gè)鎖支持一個(gè)線程對(duì)資源重復(fù)加鎖。指在同一個(gè)線程在外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖。不會(huì)因?yàn)橹耙呀?jīng)獲取過(guò)還沒(méi)釋放而阻塞。

在Java中,ReentrantLocksynchronized都是可重入鎖,可重入鎖的還有一個(gè)優(yōu)點(diǎn)是可一定程度避免死鎖。

public static void main(String[] args) {
    doOne();
}
public static synchronized  void doOne(){
    System.out.println("執(zhí)行第一個(gè)任務(wù)");
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    // 執(zhí)行第二個(gè)任務(wù)
    doTwo();
}
public static synchronized  void doTwo(){
    System.out.println("執(zhí)行第二個(gè)任務(wù)");
}

簡(jiǎn)單的測(cè)試下結(jié)果:

執(zhí)行第一個(gè)任務(wù)
執(zhí)行第二個(gè)任務(wù)

可以驗(yàn)證得到,類中的兩個(gè)方法都是被內(nèi)置鎖synchronized修飾的,而在doOne方法去調(diào)用doTwo方法時(shí),因?yàn)槭强芍厝腈i,所以同個(gè)線程下可以直接獲得當(dāng)前對(duì)象鎖,所以synchronized是可重入鎖。

而如果我們自己在繼承AQS實(shí)現(xiàn)同步器的時(shí)候,沒(méi)有考慮到占有鎖的線程再次獲取鎖的場(chǎng)景,可能就會(huì)導(dǎo)致線程阻塞,那這個(gè)就是一個(gè)非可重入鎖。

公平鎖和非公平鎖 :

這里的公平,可以按生活上來(lái)講,如果你跟你女朋友吵架,你覺(jué)得你是正確的,最后的結(jié)果卻你必須得哄你女朋友還得道歉,你信嗎?所以這是公平的嗎?

如果對(duì)一個(gè)鎖來(lái)說(shuō),先對(duì)鎖獲取請(qǐng)求的線程一定會(huì)先被滿足,后對(duì)鎖獲取請(qǐng)求的線程后被滿足,那這個(gè)鎖就是公平的。反之,那就是不公平的。

公平鎖:

多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖,線程直接進(jìn)入隊(duì)列中排隊(duì),隊(duì)列中的第一個(gè)線程才能獲得鎖。公平鎖的優(yōu)點(diǎn)是等待鎖的線程不會(huì)餓死。

缺點(diǎn)是整體吞吐效率相對(duì)非公平鎖要低,等待隊(duì)列中除第一個(gè)線程以外的所有線程都會(huì)阻塞,CPU喚醒阻塞線程的開(kāi)銷比非公平鎖大。

非公平鎖:

多個(gè)線程加鎖時(shí)直接嘗試獲取鎖,獲取不到才會(huì)到等待隊(duì)列的隊(duì)尾等待。但如果此時(shí)鎖剛好可用,那么這個(gè)線程可以無(wú)需阻塞直接獲取到鎖,所以非公平鎖有可能出現(xiàn)后申請(qǐng)鎖的線程先獲取鎖的場(chǎng)景。

非公平鎖的優(yōu)點(diǎn)是可以減少喚起線程的開(kāi)銷,整體的吞吐效率高,因?yàn)榫€程有幾率不阻塞直接獲得鎖,CPU不必喚醒所有線程。缺點(diǎn)是處于等待隊(duì)列中的線程可能會(huì)餓死,或者等很久才會(huì)獲得鎖。

在Java中,對(duì)于ReentrantLock而言,可以通過(guò)構(gòu)造函數(shù)指定該鎖是否是公平鎖,默認(rèn)是非公平鎖。

獨(dú)享鎖和共享鎖:

對(duì)于獨(dú)享和共享,這兩個(gè)概念應(yīng)該可以見(jiàn)名知意,對(duì)于MyGirl喜歡的東西,是碰都碰不得,而對(duì)于不喜歡,或者還可以的東西,可以和她共享。

獨(dú)享鎖:

也叫排他鎖,是指該鎖一次只能被一個(gè)線程所持有。如果線程B對(duì)變量A加上排它鎖后,則其他線程不能再對(duì)A加任何類型的鎖。獲得獨(dú)享鎖的線程即能讀數(shù)據(jù)又能修改數(shù)據(jù)。

在Java中,synchronized就是一種獨(dú)享鎖。

共享鎖

代表該鎖可被多個(gè)線程所持有。如果線程B對(duì)變量A加上共享鎖后,則其他線程只能對(duì)A再加共享鎖,不能加排它鎖。獲得共享鎖的線程只能讀數(shù)據(jù),不能修改數(shù)據(jù)。

總結(jié)

這篇文章就到這里了,如果這篇文章對(duì)你也有所幫助,希望您能多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • 實(shí)例解析Java關(guān)于static的作用

    實(shí)例解析Java關(guān)于static的作用

    只要是有學(xué)過(guò)Java的都一定知道static,也一定能多多少少說(shuō)出一些作用和注意事項(xiàng)。文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • spring打包到j(luò)ar包的問(wèn)題解決

    spring打包到j(luò)ar包的問(wèn)題解決

    這篇文章主要給大家介紹了關(guān)于spring打包到j(luò)ar包遇到的問(wèn)題的解決方法,文中通過(guò)實(shí)例代碼結(jié)束的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用spring打包具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • SpringMVC @RequestBody Date類型的Json轉(zhuǎn)換方式

    SpringMVC @RequestBody Date類型的Json轉(zhuǎn)換方式

    這篇文章主要介紹了SpringMVC @RequestBody Date類型的Json轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Spring容器注入bean的幾種方式詳解

    Spring容器注入bean的幾種方式詳解

    這篇文章主要介紹了Spring容器注入bean的幾種方式詳解,@Configuration用來(lái)聲明一個(gè)配置類,然后使用 @Bean 注解,用于聲明一個(gè)bean,將其加入到Spring容器中,這種方式是我們最常用的一種,需要的朋友可以參考下
    2024-01-01
  • SpringBoot HATEOAS用法簡(jiǎn)介(入門)

    SpringBoot HATEOAS用法簡(jiǎn)介(入門)

    這篇文章主要介紹了SpringBoot HATEOAS用法簡(jiǎn)介(入門),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • MyBatis-Plus如何實(shí)現(xiàn)自動(dòng)加密解密

    MyBatis-Plus如何實(shí)現(xiàn)自動(dòng)加密解密

    這篇文章主要介紹了MyBatis-Plus實(shí)現(xiàn)自動(dòng)加密解密方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 在Java項(xiàng)目中實(shí)現(xiàn)CI/CD持續(xù)集成與持續(xù)部署

    在Java項(xiàng)目中實(shí)現(xiàn)CI/CD持續(xù)集成與持續(xù)部署

    這篇文章主要為大家介紹了在Java項(xiàng)目中實(shí)現(xiàn)CI/CD持續(xù)集成與持續(xù)部署詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • Java語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)棧代碼詳解

    Java語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)棧代碼詳解

    這篇文章主要介紹了Java語(yǔ)言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)棧代碼詳解,簡(jiǎn)單介紹了棧的概念,然后分享了線性棧和鏈?zhǔn)綏5腏ava代碼,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • SpringMVC將請(qǐng)求和響應(yīng)的數(shù)據(jù)轉(zhuǎn)換為JSON格式的幾種方式

    SpringMVC將請(qǐng)求和響應(yīng)的數(shù)據(jù)轉(zhuǎn)換為JSON格式的幾種方式

    這篇文章主要給大家介紹餓了SpringMVC將請(qǐng)求和響應(yīng)的數(shù)據(jù)轉(zhuǎn)換為JSON格式的幾種方式,文中通過(guò)代碼示例和圖文結(jié)合給大家介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-11-11
  • Java IO流之原理分類與節(jié)點(diǎn)流文件操作詳解

    Java IO流之原理分類與節(jié)點(diǎn)流文件操作詳解

    流(Stream)是指一連串的數(shù)據(jù)(字符或字節(jié)),是以先進(jìn)先出的方式發(fā)送信息的通道,數(shù)據(jù)源發(fā)送的數(shù)據(jù)經(jīng)過(guò)這個(gè)通道到達(dá)目的地,按流向區(qū)分為輸入流和輸出流
    2021-10-10

最新評(píng)論