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

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

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

1. 你知道volatile是如何保證可見性嗎?

我們先看一組代碼:

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án)
                while(!initFlag) {
                }
                System.out.println("今天的世界打烊了,晚安!");
            }
        }).start();
        // 這里是為了能保證運行完上面的代碼
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 這里是Lambda表達式,就是上面的縮寫
        new Thread(() -> {
            System.out.println("準備填充數(shù)據(jù),修改initFlag的值");
            initFlag = true;
            System.out.println("準備數(shù)據(jù)完了!");
        }).start();
    }
}

運行得到的結果

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

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

那我們可以加個volatile關鍵字修飾變量試下?

 public static volatile boolean initFlag = false;

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

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

我們來看看圖解吧

先解釋下這其中連接的幾個單詞:

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

大概講一下流程:

在線程B讀取initFlag變量后,重新賦值true給變量,此時,因為加了volatile修飾,所以會馬上將值寫入到主內存中修改變量中的值,此時因為有一個cpu總線嗅探機制會監(jiān)聽到主內存的變量值發(fā)生改變了,會把本地內存的中initFlag變量設置了失效,重新讀取一邊主內存的新值,就可以達到解決變量可見性問題。這是它第一個保證可見性的關鍵。

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

因為被volatile修飾的話,它會禁止指令重排序。那它主要是依靠什么指令重排序呢?它是通過內存屏障來實現(xiàn)的。什么是內存屏障?硬件層面,內存屏障分兩種:讀屏障(Load Barrier)和寫屏障(Store Barrier)。內存屏障有兩個作用:

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

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

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

看一下示意圖

小結:

volatile作用

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

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

其實聽名字,我們就應該有個概念:

悲觀對應著我們生活中的人,悲觀的人一般看待事物都會相對消極負能量點,會盡可能往壞處去想的。這也是對應著MyGirl,她其實是一個也不能說算是悲觀的人,只能說看待事物可能會更往深入,更壞的一方面的去思考。

這其實跟我很互補,因為算是個樂天派吧,而樂觀對應著我們生活中的人,樂觀的人一般看待事物都會相對積極正能量,會盡可能往好處去想的。我其實對待生活的方方面面可能會更樂觀點,但有時帶來的一些壞處也是難以估計的。

所以說這兩者不能說誰好誰壞,只能對應著場景選擇對應的方法。

悲觀鎖:

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

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

樂觀鎖:

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

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

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

兩者對應的場景的區(qū)別:

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

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

可重入鎖和非可重入鎖:

所謂重入鎖又名遞歸鎖,顧名思義。就是支持重新進入的鎖,也就是說這個鎖支持一個線程對資源重復加鎖。指在同一個線程在外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖。不會因為之前已經獲取過還沒釋放而阻塞。

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

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

簡單的測試下結果:

執(zhí)行第一個任務
執(zhí)行第二個任務

可以驗證得到,類中的兩個方法都是被內置鎖synchronized修飾的,而在doOne方法去調用doTwo方法時,因為是可重入鎖,所以同個線程下可以直接獲得當前對象鎖,所以synchronized是可重入鎖。

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

公平鎖和非公平鎖 :

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

如果對一個鎖來說,先對鎖獲取請求的線程一定會先被滿足,后對鎖獲取請求的線程后被滿足,那這個鎖就是公平的。反之,那就是不公平的。

公平鎖:

多個線程按照申請鎖的順序來獲取鎖,線程直接進入隊列中排隊,隊列中的第一個線程才能獲得鎖。公平鎖的優(yōu)點是等待鎖的線程不會餓死。

缺點是整體吞吐效率相對非公平鎖要低,等待隊列中除第一個線程以外的所有線程都會阻塞,CPU喚醒阻塞線程的開銷比非公平鎖大。

非公平鎖:

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

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

在Java中,對于ReentrantLock而言,可以通過構造函數(shù)指定該鎖是否是公平鎖,默認是非公平鎖。

獨享鎖和共享鎖:

對于獨享和共享,這兩個概念應該可以見名知意,對于MyGirl喜歡的東西,是碰都碰不得,而對于不喜歡,或者還可以的東西,可以和她共享。

獨享鎖:

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

在Java中,synchronized就是一種獨享鎖。

共享鎖

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

總結

這篇文章就到這里了,如果這篇文章對你也有所幫助,希望您能多多關注腳本之家的更多內容!

相關文章

  • 使用java對一副撲克牌建模

    使用java對一副撲克牌建模

    這篇文章主要為大家詳細介紹了如何使用java對一副撲克牌建模,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • 教你用JAVA寫文本編輯器(二)

    教你用JAVA寫文本編輯器(二)

    之前介紹了JAVA寫文本編輯器的基本思路,下面這篇文章就來給大家介紹了關于用JAVA寫文本編輯器的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-11-11
  • SpringBoot事務異步調用引發(fā)的bug解決

    SpringBoot事務異步調用引發(fā)的bug解決

    本文主要介紹了SpringBoot事務異步調用引發(fā)的bug解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • springboot中JSONObject遍歷并替換部分json值

    springboot中JSONObject遍歷并替換部分json值

    這篇文章主要介紹了springboot中JSONObject遍歷并替換部分json值,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • MyBatis常用的jdbcType數(shù)據(jù)類型

    MyBatis常用的jdbcType數(shù)據(jù)類型

    這篇文章主要介紹了MyBatis常用的jdbcType數(shù)據(jù)類型的相關資料,需要的朋友可以參考下
    2016-12-12
  • 關于jdk9、jdk10、jdk11、jdk12、jdk13新特性說明

    關于jdk9、jdk10、jdk11、jdk12、jdk13新特性說明

    這篇文章主要介紹了關于jdk9、jdk10、jdk11、jdk12、jdk13新特性說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 淺談SpringMVC HandlerInterceptor詭異問題排查

    淺談SpringMVC HandlerInterceptor詭異問題排查

    這篇文章主要介紹了淺談SpringMVC HandlerInterceptor詭異問題排查,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • 基于Java網絡編程和多線程的多對多聊天系統(tǒng)

    基于Java網絡編程和多線程的多對多聊天系統(tǒng)

    這篇文章主要介紹了基于Java網絡編程和多線程的多對多聊天系統(tǒng),文中有非常詳細的代碼示例,對正在學習java網絡編程的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • 深入了解Java線程池:從設計思想到源碼解讀

    深入了解Java線程池:從設計思想到源碼解讀

    這篇文章將從設計思想到源碼解讀,帶大家深入了解Java的線程池,文中的示例代碼講解詳細,對我們的學習或工作有一定的幫助,需要的可以參考一下
    2021-12-12
  • 關于Mybatis的mapper接口函數(shù)重載問題

    關于Mybatis的mapper接口函數(shù)重載問題

    這篇文章主要介紹了關于Mybatis的mapper接口函數(shù)重載問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評論

彭州市| 沾化县| 任丘市| 惠东县| 乐安县| 福清市| 宾阳县| 海口市| 青铜峡市| 康平县| 文成县| 淳安县| 甘谷县| 鲁甸县| 宝应县| 灵宝市| 宿迁市| 延寿县| 白城市| 上思县| 信阳市| 溧水县| 仙游县| 南郑县| 铅山县| 璧山县| 景泰县| 菏泽市| 伊春市| 共和县| 周至县| 抚宁县| 赫章县| 施甸县| 家居| 兴文县| 巨鹿县| 博爱县| 广宗县| 黄梅县| 开阳县|