亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java的鎖機(jī)制:synchronized和CAS詳解

 更新時(shí)間:2021年09月28日 15:15:21   作者:我是坑貨  
這篇文章主要介紹了Java的鎖機(jī)制synchronized和CAS詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

提到Java的知識(shí)點(diǎn)一定會(huì)有多線程,JDK版本不斷的更迭很多新的概念和方法也都響應(yīng)提出,但是多線程和線程安全一直是一個(gè)重要的關(guān)注點(diǎn)。比如說我們一入門就學(xué)習(xí)的synchronized怎么個(gè)實(shí)現(xiàn)和原理,還有總是被提到的CAS是啥,他和synchronized關(guān)系是啥?這里大概會(huì)讓你對(duì)這些東西有一個(gè)認(rèn)識(shí)。

一 為什么要用鎖

我們使用多線程肯定是為了提高效率,壓榨硬件的性能提高效率,假設(shè)多一個(gè)線程相當(dāng)于多一個(gè)人干活,但是有時(shí)候人多了就不是很好管理,可能出現(xiàn)問題。

比如我現(xiàn)在搞一個(gè)多線程的demo,我的本意是每個(gè)線程都高呼“ZPNB!”,我寫下了如下的代碼。

public class ThreadDemo implements Runnable{
    @Test
    public void testThread() {
        System.out.println("大聲告訴我:");
        ThreadDemo demo =  new ThreadDemo();
        Thread threadOne = new Thread(demo,"張三:ZPNB");
        Thread threadTwo = new Thread(demo,"李四:ZPNB");
        Thread threadThree = new Thread(demo,"王二麻子:ZPNB");
        Thread threadFour = new Thread(demo,"趙四:ZPNB");
        threadOne.start();
        threadTwo.start();
        threadThree.start();
        threadFour.start();
    }
    @Override
    public void run() {
//        synchronized (this){
            for( int i = 0; i < 10 ;i++  ){
                try {
                    System.out.println(Thread.currentThread().getName());
                    //這里設(shè)置0是因?yàn)镴unit的限制你設(shè)置長(zhǎng)了,他就執(zhí)行一段時(shí)間就不執(zhí)行了
                    Thread.sleep(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
//        }
    }

沒有加鎖的情況是這樣的,看起來很亂我希望他們每個(gè)人都喊十遍然后下一個(gè)人,顯然下面的結(jié)果不滿意

大聲告訴我:
李四:ZPNB
張三:ZPNB
李四:ZPNB
張三:ZPNB
李四:ZPNB
張三:ZPNB
張三:ZPNB
李四:ZPNB
張三:ZPNB
李四:ZPNB
張三:ZPNB
李四:ZPNB
張三:ZPNB
李四:ZPNB
張三:ZPNB
李四:ZPNB
張三:ZPNB
李四:ZPNB
張三:ZPNB
李四:ZPNB
王二麻子:ZPNB
趙四:ZPNB
王二麻子:ZPNB
趙四:ZPNB
王二麻子:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
趙四:ZPNB
王二麻子:ZPNB
趙四:ZPNB
王二麻子:ZPNB
趙四:ZPNB
王二麻子:ZPNB
趙四:ZPNB
王二麻子:ZPNB
趙四:ZPNB
王二麻子:ZPNB

但是如果我把synchronized的注釋取消就變成了我想要的依次每人喊十遍

大聲告訴我:
張三:ZPNB
張三:ZPNB
張三:ZPNB
張三:ZPNB
張三:ZPNB
張三:ZPNB
張三:ZPNB
張三:ZPNB
張三:ZPNB
張三:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
趙四:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
王二麻子:ZPNB
李四:ZPNB
李四:ZPNB
李四:ZPNB
李四:ZPNB
李四:ZPNB
李四:ZPNB
李四:ZPNB
李四:ZPNB
李四:ZPNB
李四:ZPNB

這就突出了鎖的重要性,我們希望有些線程能按照我們希望的一個(gè)順序依次來執(zhí)行,而不是先到先得的。

二 synchronized怎么實(shí)現(xiàn)的

實(shí)際上每一個(gè)對(duì)象實(shí)際都擁有一個(gè)叫做監(jiān)視器monitor的東西,線程只有獲得了這個(gè)監(jiān)視器才能才能進(jìn)入同步塊和同步方法,如果沒有獲取到監(jiān)視器的線程將會(huì)被阻塞在同步塊和同步方法的入口處,具體過程如下圖:

在這里插入圖片描述

那如果沒獲取到監(jiān)視器怎么辦,有個(gè)同步隊(duì)列的東西,你沒得到監(jiān)視器就等一等,等上一個(gè)獲取監(jiān)視器的exit推出監(jiān)視器你再根據(jù)隊(duì)列順序去再獲取,當(dāng)然可能在這個(gè)再獲取的過程碰到一個(gè)“新來的”沒進(jìn)隊(duì)列直接跟你搶,你還沒搶過,那你就還要重復(fù)之前的等待過程。

其實(shí)這里還涉及一個(gè)鎖的“happen before”的概念(“ A hapen-bfore B,那么 A 的結(jié)果對(duì) B 是可見的”),就是上一個(gè)線程如果對(duì)某些值有改寫,后一個(gè)應(yīng)該在這個(gè)基礎(chǔ)上改寫的原則,假設(shè)一個(gè)計(jì)算程序,值都改了,新的線程你還在拿原先的值再去計(jì)算是不對(duì)的,應(yīng)該是在新的值上面再去做操作,這樣多線程協(xié)作才有實(shí)際意義。

以下是關(guān)于synchronized作用范圍(基本是實(shí)際對(duì)象或者是類對(duì)象,如果你是類對(duì)象的話,那你new多少個(gè)實(shí)例對(duì)象還是被鎖的。)

在這里插入圖片描述

三 CAS來者何人

CAS突然這個(gè)概念出來作為線程安全的一個(gè)實(shí)現(xiàn)方式出現(xiàn),那它和synchronized是一個(gè)什么樣的關(guān)系呢?

實(shí)際二者應(yīng)該是同級(jí)的概念,大家都是鎖,synchronized是悲觀鎖,基本就是來一個(gè)線程就是鎖起來,阻塞同步的。認(rèn)為任何操作都有可能是沖突,所以按照最壞的情況來處理,線程競(jìng)爭(zhēng)阻塞了就阻塞,阻塞結(jié)束了就喚醒阻塞的進(jìn)程。

CAS就是compare and swap ,不是直接鎖起來,大概意思就是:
CAS(V,O,N),包含三個(gè)值分別為:V 內(nèi)存地址存放的實(shí)際值;O 預(yù)期的值(舊值);N 更新的新值。當(dāng)V和O相同時(shí),也就是說舊值和內(nèi)存中實(shí)際的值相同表明該值沒有被其他線程更改過,即該舊值O就是目前來說最新的值了,自然而然可以將新值N賦值給V。反之,V和O不相同,表明該值已經(jīng)被其他線程改過了則該舊值O不是最新版本的值了,所以不能將新值N賦給V,返回V即可。當(dāng)多個(gè)線程使用CAS操作一個(gè)變量是,只有一個(gè)線程會(huì)成功,并成功更新,其余會(huì)失敗。失敗的線程會(huì)重新嘗試,當(dāng)然也可以選擇掛起線程

CAS對(duì)于線程競(jìng)爭(zhēng)沖突的情況相對(duì)來說就溫柔一些,他會(huì)有自己的重試機(jī)制,就是這次不行我等一會(huì)再去看看,而不是直接阻塞掛起再喚醒的狀態(tài),這樣太耗費(fèi)時(shí)間了。

在Java.util,ConCurrent包里面很多都是用CAS來處理同步的問題,而不是直接來個(gè)synchronized來修飾。

四synchronized和CAS孰優(yōu)孰劣

實(shí)際上現(xiàn)在來看,還真不好說,因?yàn)樵贑AS的方案提出,實(shí)際上synchronized也是不斷的進(jìn)步的。不能說CAS一定比synchronized好。

比如說CAS也會(huì)有自己的問題,最主要的有:ABA,自旋時(shí)間過長(zhǎng)和只能保證一個(gè)共享變量的原子操作,雖然說都要相關(guān)的解決方案:

(1)ABA就是兩個(gè)線程第一個(gè)線程將最開始的A值改成B再改成A,第二個(gè)線程接手直接CAS,會(huì)得不到之前的轉(zhuǎn)換的過程,解決方式跟數(shù)據(jù)庫(kù)一樣加一個(gè)版本號(hào)1A 2B 3C解決。

(2)自旋時(shí)間過長(zhǎng)就是線程競(jìng)爭(zhēng)沖突,不停地重試,實(shí)際是一個(gè)循環(huán)操作,這個(gè)循環(huán)可能要等好長(zhǎng)時(shí)間,導(dǎo)致所謂的自旋時(shí)間過長(zhǎng)。

(3)只能操作一個(gè)共享原子,就讓這個(gè)原子變成一個(gè)對(duì)象,把要共享的都塞進(jìn)去。

synchronized自身也在不斷地優(yōu)化自身,甚至也借鑒了CAS的思想在1.6里面。為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”,在Java SE 1.6中,鎖一共有4種狀態(tài),級(jí)別從低到高依次是:無(wú)鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)和重量級(jí)鎖狀態(tài)。

偏向鎖(通過線程ID來看對(duì)象頭和棧幀里面查找線程ID(記錄的線程ID就是偏向的線程ID),有就獲取沒有就嘗試CAS設(shè)置自己為偏向的線程)

具體如下:

當(dāng)一個(gè)線程訪問同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)鎖偏向的線程ID,以后該線程在進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作來加鎖和解鎖,只需簡(jiǎn)單地測(cè)試一下對(duì)象頭的Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。如果測(cè)試成功,表示線程已經(jīng)獲得了鎖。如果測(cè)試失敗,則需要再測(cè)試一下Mark Word中偏向鎖的標(biāo)識(shí)是否設(shè)置成1(表示當(dāng)前是偏向鎖),如果沒有設(shè)置,則使用CAS競(jìng)爭(zhēng)鎖;如果設(shè)置了,則嘗試使用CAS將對(duì)象頭的偏向鎖指向當(dāng)前線程。

輕量級(jí)鎖

(替換鎖的指針替換成就獲得鎖,替換不成就自旋循環(huán)去找機(jī)會(huì)替換)

具體如下:

線程在執(zhí)行同步塊之前,JVM會(huì)先在當(dāng)前線程的棧楨中創(chuàng)建用于存儲(chǔ)鎖記錄的空間,并將對(duì)象頭中的Mark Word復(fù)制到鎖記錄中。然后線程嘗試使用CAS將對(duì)象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當(dāng)前線程獲得鎖,如果失敗,表示其他線程競(jìng)爭(zhēng)鎖,當(dāng)前線程便嘗試使用自旋來獲取鎖。

重量級(jí)鎖

(monitor監(jiān)視器鎖的實(shí)現(xiàn),最重的一步,因?yàn)樯婕暗接脩魬B(tài)和系統(tǒng)態(tài)切換。)

重量級(jí)鎖是依賴對(duì)象內(nèi)部的monitor鎖來實(shí)現(xiàn)。當(dāng)系統(tǒng)檢查到鎖是重量級(jí)鎖之后,會(huì)把等待想要獲得鎖的線程進(jìn)行阻塞,被阻塞的線程不會(huì)消耗cup。但是阻塞或者喚醒一個(gè)線程時(shí),都需要操作系統(tǒng)來幫忙,需要從用戶態(tài)轉(zhuǎn)換到內(nèi)核態(tài),而轉(zhuǎn)換狀態(tài)是需要消耗很多時(shí)間。

這么看來synchronized并不是那么不堪,未必你用CAS實(shí)現(xiàn)的就一定在某些環(huán)境比synchronized這個(gè)“元老”強(qiáng)。

總結(jié)

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

相關(guān)文章

  • Java和C語(yǔ)言分別實(shí)現(xiàn)水仙花數(shù)及拓展代碼

    Java和C語(yǔ)言分別實(shí)現(xiàn)水仙花數(shù)及拓展代碼

    這篇文章主要介紹了分別用Java和C語(yǔ)言實(shí)現(xiàn)水仙花數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-11-11
  • 最新評(píng)論