国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

多線程(JavaEE初階系列7)

這篇具有很好參考價值的文章主要介紹了多線程(JavaEE初階系列7)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

目錄

前言:

1.常見的鎖策略

1.1樂觀鎖和悲觀鎖

1.2輕量級鎖和重量級鎖

1.3自旋鎖和掛起等待鎖

1.4互斥鎖與讀寫鎖

1.5可重入鎖與不可重入鎖

1.6公平鎖與非公平鎖

2.CAS

2.1什么是CAS

2.2自旋鎖的實(shí)現(xiàn)

2.3原子類

3.synchronized

3.1synchronized的原理以及基本特點(diǎn)

3.2偏向鎖

3.3輕量級鎖

3.4重量級鎖

3.5鎖消除

3.6鎖粗化

4.JUC

4.1JUC中常見到組件

4.1.1callable接口的用法

4.1.2ReentrantLock可重入互斥鎖

4.1.3信號量Semaphore

4.1.4CountDownLatch

5.線程安全的集合類

5.1HashTable

5.2ConcurrentHashMap

5.3ConcurrentHashMap與HashMap的區(qū)別

6.死鎖

6.1什么是死鎖

6.2哲學(xué)家就餐問題

結(jié)束語:


前言:

在上一節(jié)中小編主要是與大家分享了多線程中的線程池的概念以及使用方式,在之前的多線程博客中也個大家介紹了多線程中的一些基礎(chǔ)知識,希望大家下去之后多多練習(xí),鞏固一下,那么從這節(jié)開始小編就開始給大家介紹多線程中的最后一點(diǎn)知識,雖然這些東西不常使用,但是我們還是需要稍微的理解一下的。話不多說我們直接步入正題吧!

1.常見的鎖策略

以下介紹的鎖策略不只是針對java的,別的語言別的工具也會涉及到鎖,也同樣適合用。

1.1樂觀鎖和悲觀鎖

鎖的實(shí)現(xiàn)者會預(yù)測接下來鎖沖突的概率是大還是不大,根據(jù)這個沖突的概率來決定接下來該咋做。這里的鎖的沖突就是鎖競爭,兩個線程在針對一個對象加鎖,另一個就會產(chǎn)生阻塞等待。

這里我們就根據(jù)預(yù)測鎖沖突的概率大小的問題來將鎖分為樂觀鎖和悲觀鎖。

  • 樂觀鎖:預(yù)測接下來沖突的概率不大。假設(shè)數(shù)據(jù)一般情況下不會產(chǎn)生并發(fā)沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時候,才會正式的對數(shù)據(jù)是否產(chǎn)生并發(fā)沖突進(jìn)行檢測,如果發(fā)生并發(fā)沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。
  • 悲觀鎖:預(yù)測接下來的沖突概率比較大??偸羌僭O(shè)最壞的情況,每次去拿數(shù)據(jù)的時候都認(rèn)為別人會去修改,所以在每次拿數(shù)據(jù)的時候都會鎖上,這樣別人想拿數(shù)據(jù)就會阻塞直到它拿到鎖。

通常來說悲觀鎖做的工作要多一些,但是效率會更低一些,而樂觀鎖做的工作少一些,但是工作的效率會高一些。

1.2輕量級鎖和重量級鎖

  • 輕量級鎖:加鎖解鎖過程更快更高效。少量的內(nèi)核態(tài)用戶態(tài)切換,不太容易引起線程調(diào)度。
  • 重量級鎖:加鎖解鎖過程更慢更低效。大量的內(nèi)核態(tài)用戶態(tài)切換。很容易引發(fā)線程調(diào)度。

和樂觀和悲觀雖然不是一回事,但是也有一定的重合,一個樂觀鎖很有可能也是一個輕量級鎖,一個悲觀鎖很可能也是一個重量級鎖。

1.3自旋鎖和掛起等待鎖

  • 自旋鎖:他是輕量級鎖的一種典型實(shí)現(xiàn)。在鎖競爭中,如果獲取鎖失敗,立即再次嘗試獲取鎖,無限循環(huán),直到獲取到鎖為止,第一次獲取鎖失敗,第二次的嘗試會在極短的時間內(nèi)到來。一旦鎖被其他線程釋放,就能第一時間獲取到鎖。
  • 掛起等待鎖:是重量級鎖的一種典型實(shí)現(xiàn)。還是上面的鎖競爭,如果第一次嘗試加鎖失敗,就會進(jìn)入阻塞狀態(tài),需要過很久才會再次調(diào)度。

自旋鎖是一種典型的輕量級鎖的實(shí)現(xiàn)方式。

  • 優(yōu)點(diǎn):沒有放棄CPU,不涉及線程阻塞和調(diào)度,一旦被釋放就能第一時間獲取到鎖。
  • 缺點(diǎn):如果鎖被其他線程持有的時間比較久,那么就會持續(xù)消耗CPU資源(而掛起等待的時候是不消耗CPU的)。

其中我們之前學(xué)習(xí)的synchronized即是悲觀鎖也是樂觀鎖,即是輕量級鎖也是重量級鎖,輕量級鎖部分基于自旋鎖實(shí)現(xiàn),重量級鎖部分基于掛起等待鎖實(shí)現(xiàn)。

那么究竟什么時候是樂觀鎖,什么時候是悲觀鎖,什么時候是輕量級鎖,什么時候是重量級的鎖,這里synchronized會根據(jù)當(dāng)前鎖競爭的激烈程度自適應(yīng)。如果鎖沖突不激烈,就以輕量級/樂觀鎖的狀態(tài)運(yùn)行,如果鎖沖突激烈,以重量級鎖/悲觀鎖的狀態(tài)運(yùn)行。

1.4互斥鎖與讀寫鎖

  • 互斥鎖:像我們之前學(xué)習(xí)的synchronized就是一種互斥鎖,synchronized只有兩個操作,進(jìn)去代碼塊就加鎖,出代碼塊就解鎖。
  • 讀寫鎖:就是對讀操作和寫操作分別進(jìn)行加鎖。讀寫鎖有三步:給讀加鎖,給寫加鎖,解鎖。

在讀寫鎖中約定:

  • 讀鎖和寫鎖之前不會產(chǎn)生鎖競爭,不會產(chǎn)生阻塞等待。
  • 寫鎖和寫鎖之間有所競爭。
  • 讀鎖和寫鎖之間也有鎖競爭。

讀寫鎖特別適合于“頻繁讀,不頻繁寫”的場景中。

1.5可重入鎖與不可重入鎖

  • 可重入鎖:如果一個鎖,在一個線程中,連續(xù)對該鎖連續(xù)兩次或者是多次加鎖,不發(fā)生死鎖,就叫可重入鎖。
  • 不可重入鎖:如果在上述連續(xù)加鎖兩次或者是多次的情況下發(fā)生了死鎖就叫不可重入鎖。

在我們Java里面只要是以Reentrant開頭命名的鎖都是可重入鎖,而且JDK提供所有現(xiàn)成的Lock實(shí)現(xiàn)類,包括synchronized關(guān)鍵字都是可重入鎖。?

1.6公平鎖與非公平鎖

  • 公平鎖:遵守“先來后到”,就是公平鎖。
  • 非公平鎖:不遵守“先來后到”的就是非公平鎖。

注意:

  • 操縱系統(tǒng)內(nèi)部的線程調(diào)度就可以視為是隨機(jī)的,如果不作任何額外的限制,鎖就是非公平的,如果要想實(shí)現(xiàn)公平鎖,就需要依賴額外的數(shù)據(jù)結(jié)構(gòu),來記錄線程們的先后順序。
  • 公平鎖和非公平鎖沒有好壞之分,關(guān)鍵還是看適合場景。
  • 我們之前學(xué)習(xí)的synchronized就是一個非公平鎖。

2.CAS

2.1什么是CAS

CAS的全稱是:Compare?and?swap,字面意思就是“比較并交換”,一個CAS涉及到以下操作:

我們假設(shè)內(nèi)存中的原數(shù)據(jù)V,舊的預(yù)期值A(chǔ),需要修改的新值B。步驟如下所示:

  1. 比較A與V是否相等。(比較)
  2. 如果比較相等,將B寫入V。(交換)
  3. 返回操作是否成功。

CAS就相當(dāng)于是給我們打開了新世界的大門讓我們不需要加鎖,就能夠保證線程的安全。基于CAS可以實(shí)現(xiàn)很多操作,具體的我們往下看。

2.2自旋鎖的實(shí)現(xiàn)

偽代碼如下所示:

public class SpinLock {
    private Thread owner = null;

    public void lock(){
        //通過CAS看當(dāng)前鎖是否被某個線程持有。
        //如果這個鎖已經(jīng)被別的線程持有,那么就自旋等待。
        //如果這個鎖沒有被別的線程持有,那么就把owner設(shè)為當(dāng)前嘗試加鎖的線程。
        while (!CAS(this.owner,null,Thread.currentThread())){

        }
    }
    
    public void unlock() {
        this.owner = null;
    }
}

在上述代碼中如果當(dāng)前owner是null,比較就成功,就把當(dāng)前線程的引用設(shè)置到owner中,加鎖完成,循環(huán)就會結(jié)束,如果比較不成功,意味著owner非空,鎖已經(jīng)有線程持有了,此時CAS就啥也不干,直接返回false,循環(huán)繼續(xù)。此時的循環(huán)就會轉(zhuǎn)的飛快,不停的嘗試詢問這里的鎖是不是釋放了,它的好處就是一旦釋放,就會立即獲取到,壞處就是CPU此時就會處于一種忙等的狀態(tài)。?

2.3原子類

在標(biāo)準(zhǔn)庫中提供了java.util.concurrent.atomic包,里面的都是基于這種方式來實(shí)現(xiàn)的,典型的類就是:AtomicInteger類,其中g(shù)etAndIncrement就相當(dāng)于是++操作,他就能夠保證在++?和 -- 的時候線程是安全的。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

圖例演示:
先讀入內(nèi)存中:
多線程(JavaEE初階系列7),JavaEE初階,java-ee,java?

將自增的結(jié)果寫回到CPU中。?

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java?

另一個先判斷要自增的數(shù)據(jù)是不是和之前讀取到的數(shù)據(jù)一樣,如果不一樣則重新讀入,再自增。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java?

接下來我們就用代碼給大家具體來演示一下:

代碼展示:

package Thread;

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadTest02 {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger num = new AtomicInteger(0);
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                //實(shí)現(xiàn)自增
                num.getAndIncrement();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                //實(shí)現(xiàn)自增
                num.getAndIncrement();
            }
        });
        
        t1.start();
        t2.start();

        t1.join();
        t2.join();

        //get來獲取到數(shù)值
        System.out.println(num.get());
    }
}


結(jié)果展示:

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

?

3.synchronized

3.1synchronized的原理以及基本特點(diǎn)

上面也給大家大概的有提到synchronized的基本特點(diǎn),這里小編再給大家總結(jié)一下:

synchronized的特點(diǎn):

  • 即是樂觀鎖又是悲觀鎖。開始時是樂觀鎖,如果鎖沖突頻繁,就轉(zhuǎn)換為悲觀鎖。
  • 即是輕量級鎖又是重量級鎖。開始時輕量級鎖,如果鎖被持有時間較長,就轉(zhuǎn)換為重量級鎖。
  • 輕量級鎖基于自旋鎖的實(shí)現(xiàn),重量級鎖基于掛起等待鎖的實(shí)現(xiàn)。
  • 不是讀寫鎖。
  • 是可重入鎖。
  • 是非公平鎖。

JVM將synchronized鎖分為無鎖、偏向鎖、輕量級鎖、重量級鎖狀態(tài),會根據(jù)情況進(jìn)行依次升級。

synchronized的關(guān)鍵策略:鎖升級。

加鎖的工作過程如下所示:

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

下面我們來給大家來分別解釋一下上面的鎖都是什么。??

3.2偏向鎖

第一個嘗試加鎖的線程,優(yōu)先進(jìn)入偏向鎖的狀態(tài)。偏向鎖不是真的“加鎖”,只是給對象頭中做一個“偏向鎖的標(biāo)記”,記錄這個鎖屬于哪個線程。如果后續(xù)沒有其他線程來競爭該鎖,那么就不用子啊進(jìn)行其他操作了(避免了加鎖解鎖的開銷)如果后續(xù)有其他線程來競爭該鎖(剛才已經(jīng)在鎖對象中記錄了當(dāng)前鎖屬于那哪個線程了,很容易識別當(dāng)前申請鎖的線程是不是之前記錄的線程),那就取消原來的偏向鎖狀態(tài),進(jìn)入一般的輕量級鎖狀態(tài)。

偏向鎖的本質(zhì)上相當(dāng)于“延遲加鎖”,能不加鎖就不加鎖,“非必要,不加鎖”,盡量避免不必要的加鎖開銷。但是該做的標(biāo)記還是得做,否則無法區(qū)分何時需要真正加鎖。

3.3輕量級鎖

隨著其他線程進(jìn)入競爭,偏向鎖狀態(tài)被消除,進(jìn)入輕量級鎖狀態(tài)(自適應(yīng)的自旋鎖),此處的輕量級鎖就是通過CAS來實(shí)現(xiàn)。

  • 通過CAS檢查并更新一塊內(nèi)存(比如null =>?該線程引用)
  • 如果更新成功,則認(rèn)為加鎖成功。
  • 如果更新失敗,則認(rèn)為鎖被占用,繼續(xù)自旋式的等待(并不放棄CPU)。

3.4重量級鎖

如果競爭進(jìn)一步激烈,自旋不能快速獲取到鎖狀態(tài),就會膨脹為重量級鎖。

  • 執(zhí)行加鎖操作,先進(jìn)入內(nèi)核態(tài)。
  • 在內(nèi)核態(tài)判定當(dāng)前鎖是否已經(jīng)被占用。
  • 如果該鎖沒有占用,則加鎖成功,并切回用戶態(tài)。
  • 如果該鎖被占用,則加鎖失敗,此時線程進(jìn)入鎖的等待的隊(duì)列掛起,等待被操作系統(tǒng)喚醒。
  • 經(jīng)歷了一系列的滄海桑田,這個鎖被其他線程釋放了,操作系統(tǒng)也想起了這個掛起的線程,于是喚醒這個線程,嘗試重新獲取鎖。

3.5鎖消除

鎖消除也是“非必要,不加鎖”,他是在編譯階段做的優(yōu)化手段,用來檢測當(dāng)前代碼是否是多線程執(zhí)行/是否有必要加鎖,如果不必要,又已經(jīng)把鎖給寫了,那么就會在編譯階段中將鎖自動去掉。

3.6鎖粗化

首先來解釋一下什么是鎖是粒度,簡單來說就是在synchronized中包含代碼的多少,如果包含的代碼越多,粒度越粗,越少則粒度越細(xì)。一般我們在寫代碼的時候多數(shù)情況下是希望粒度更小一點(diǎn),(串行執(zhí)行的代碼少,并發(fā)執(zhí)行的代碼就多,效率就高)。下面我們可以畫個圖來解釋一下。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java?

就比如說在公司中如果你要給領(lǐng)導(dǎo)匯報工作,兩種情況:

情況1:
????????先打電話匯報工作A的進(jìn)展,掛電話。

????????再打電話匯報工作B的進(jìn)展,掛電話。

????????最后打電話匯報工作C的進(jìn)展,掛電話。

情況2:

????????打一個電話匯報工作A、B、C的進(jìn)展,掛電話。?

上述的情況1就相當(dāng)于粒度細(xì)的,情況2相當(dāng)于是粒度粗的情況。

實(shí)際開發(fā)過程中,使用細(xì)粒度鎖,是期望釋放鎖的時候其他線程能使用鎖。但是實(shí)際上可能并沒有其他線程來搶占這個鎖,這種情況下JVM就會自動把鎖粗化,避免頻繁申請釋放鎖。

4.JUC

JUC是java.util.concurrent的縮寫。下面我來看下在里面都有哪些常用到的組件吧!

4.1JUC中常見到組件

4.1.1callable接口的用法

Callable是一個interface,相當(dāng)于把線程封裝了一個“返回值”,方便程序猿借助多線程的方式計算結(jié)果。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

他會讓你重寫call方法,在上述中泛型的參數(shù)是啥,call反回的就是啥。?

下面我們來寫一個代碼,創(chuàng)建一個線程,用這個線程來計算:1 + 2 + 3.....+1000。

代碼展示:

package Thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadTest03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //這里只是創(chuàng)建了一個任務(wù)
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 1000; i++) {
                    sum += i;
                }
                return sum;
            }
        };
        //創(chuàng)建完任務(wù)之后我們還需要找個人來執(zhí)行這個任務(wù)。
        //Thread不能直接傳callable,需要再來包裝一層。
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();
        System.out.println(futureTask.get());
    }
}

結(jié)果展示:
多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

在上述代碼中需要我們注意的是FutureTask這個類的使用。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

這里面它將我們上述創(chuàng)建的任務(wù)傳遞給futuretask,這里的futuretask?就像是我在吃飯的時候服務(wù)員一般會給我們一張小票,然后等待飯做好之后我們在憑借著這張小票來獲取我們的飯菜,這里也一樣,執(zhí)行完任務(wù)之后,我們就可以憑借著futuretask來獲取我們的任務(wù)的結(jié)果了。這里的futuretask有一個方法是get,通過get方法就可以獲取到我們上述任務(wù)call方法的返回值了。

在之前我們給大家交代了三種創(chuàng)建多線程的方法:繼承自Thread,實(shí)現(xiàn)Runable,基于lambda三種方法,這里我們又學(xué)習(xí)了Callable。

4.1.2ReentrantLock可重入互斥鎖

我們之前學(xué)習(xí)的synchronized關(guān)鍵字是基于代碼塊的方式來控制加鎖解鎖的。ReentrantLock則是提供了lock和unlock獨(dú)立的方法,來進(jìn)行加鎖和解鎖的。雖然我們在大部分情況下使用synchronized就足夠了,但是此處的ReentrantLock也是一個重要的補(bǔ)充。

synchronized與ReentrantLock的區(qū)別:

  • synchronized只是加鎖和解鎖,加鎖的時候如果發(fā)現(xiàn)鎖被占用,只能阻塞式等待。ReentrantLock還提供了一個tryLock方法,如果加鎖成功,就沒啥特別的,如果加鎖失敗,是不會阻塞的,直接會返回false,直接把問題拋給程序猿,讓程序猿來靈活的控制。
  • synchronized是一個非公平鎖(概率不等,不遵守先來后到的)。而ReentrantLock提供了公平和非公平兩種工作模式(在構(gòu)造方法中,傳入true開啟公平鎖)。
  • synchronized搭配wait?notify進(jìn)行等待喚醒,如果多個線程同時wait同一個對象,notify的時候是隨機(jī)喚醒一個,而ReentrantLock則是搭配Condition這個類,這個類也能起到等待通知,可以使得功能更強(qiáng)大。

4.1.3信號量Semaphore

信號量,用來表示“可用資源的個數(shù)”,本質(zhì)上就是一個計數(shù)器。可以把信號量想象成是停車場的展示牌,當(dāng)前有車位100個,表示有100個可用資源。當(dāng)有車開進(jìn)去的時候,就相當(dāng)于申請一個可用資源,可用車位就-1(這個稱為信號量的P操作),當(dāng)有車開出來的時候,就相當(dāng)于釋放了一個可用資源,可用車位就+1(這個稱為信號量的V操作),如果計數(shù)器的值已近為0了,還嘗試申請資源,就會阻塞等待,直到其他線程釋放資源。

Semaphore的PV操作中的加減計數(shù)器操作都是原子的,可以子啊多線程環(huán)境下直接使用。

4.1.4CountDownLatch

CountDownLatch操作就是等待N個任務(wù)執(zhí)行結(jié)束。好像跑步比賽,10位選手依次就位,哨聲吹響之后才能出發(fā),當(dāng)所有選手都通過終點(diǎn)線之后,才算比賽結(jié)束。

5.線程安全的集合類

5.1HashTable

我們之前學(xué)習(xí)過HashMap,他在多線程的環(huán)境下是線程不安全的。這里我們就可以使用HashTable來保證線程的安全性。

不過我們進(jìn)入HashTable的原碼中可以發(fā)現(xiàn)他只是在關(guān)鍵方法上加上了synchronized。如下所示:

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java?

HashTable是針對整個哈希表進(jìn)行加鎖,任何增刪查改操作,都會被觸發(fā)加鎖,也就都會可能有鎖競爭。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

?

所以接下來我們還有另一種解決辦法就是在使用ConcurrentHashMap。

5.2ConcurrentHashMap

相比于HashTable作出了一系列的改進(jìn)和優(yōu)化,我們這里以Java1.8為例。

  • 讀操作沒有加鎖,但是使用了volatile保證從內(nèi)存中讀取結(jié)果,只對寫操作進(jìn)行加鎖,加鎖的方式仍然是用synchronized,但是不是整個對象加鎖,而是“鎖桶”(用每一個鏈表的頭結(jié)點(diǎn)作為鎖對象),大大降低了鎖沖突的概率。
  • 充分利用CAS特性,比如size屬性通過CAS來更新,避免出現(xiàn)重量級鎖的情況。
  • 優(yōu)化了擴(kuò)容的方式:化整為零
  • 發(fā)現(xiàn)需要擴(kuò)容的線程,只需要創(chuàng)建一個新的數(shù)組,同時只搬幾個元素過去。
  • 擴(kuò)容期間,新老數(shù)據(jù)同時存在
  • 后續(xù)每個來操作ConcurrentHashMap的線程,都會參與搬家的過程,每個操作負(fù)責(zé)搬運(yùn)一小部分元素。
  • 搬完最后一個元素在把老數(shù)組刪掉。
  • 這個期間,插入只往新數(shù)組加。
  • 這個期間,查找需要同時查新數(shù)組和老數(shù)組。

ConcurrentHashMap不只是一把鎖,它是將每一個鏈表的頭結(jié)點(diǎn)作為一把鎖,每一次進(jìn)行操作,都是針對對應(yīng)的鏈表的鎖進(jìn)行加鎖,操作不同鏈表就是針對不同的鎖加鎖,就不會有鎖沖突了。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

?

5.3ConcurrentHashMap與HashMap的區(qū)別

  • HashMap:是線程不安全的,key允許為null。
  • HashTable:線程安全,使用synchronized鎖HashTable對象,效率比較低,key不允許為null。
  • ConcurrentHashMap:線程安全,使用synchronized鎖每一個鏈表的頭結(jié)點(diǎn),鎖沖突效率低,充分利用CAS機(jī)制,優(yōu)化了擴(kuò)容方式,key不允許為null。

6.死鎖

6.1什么是死鎖

死鎖是這樣的一種情形:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放,由于線程被無限期地阻塞,因此程序不可能正常終止。

關(guān)于死鎖的情況:

  • 一個線程,一把鎖,可重入鎖沒事,不可重入鎖就會出現(xiàn)死鎖。
  • 兩個線程,兩把鎖,即使是可重入鎖也會死鎖。
  • N個線程,M把鎖,線程數(shù)量和鎖數(shù)量更多了就更容易出現(xiàn)死鎖了。

6.2哲學(xué)家就餐問題

此時就會出現(xiàn)一個經(jīng)典的問題:哲學(xué)家就餐問題。如下所示:

  • 有一個桌子,圍著一圈哲學(xué)家,桌子中間放著一盤面,每個哲學(xué)家兩兩之間,放著一根筷子,每個哲學(xué)家只做兩件事情:思考人生或者是吃面,思考人生的時候就放下筷子,吃面條的時候就會拿起左右兩邊的筷子(先拿起左邊,再拿起右邊)。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

?

  • 如果哲學(xué)家發(fā)現(xiàn)筷子拿不起來(被別人占用了),就會阻塞等待。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

?

  • 假設(shè)同一時刻,五個哲學(xué)家同時拿起左手邊的筷子,然后再嘗試拿右手邊的筷子,就會發(fā)現(xiàn)右手邊的筷子都被占用了,由于哲學(xué)家們都不互讓,這個時候就形成了死鎖。

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

死鎖的四個必要條件:

  • 互斥使用:一個線程拿到一把鎖之后,另一個線程不能使用。(鎖的基本特點(diǎn))
  • 不可搶占:一個線程拿到鎖,只能自己主動釋放,不能是被其他線程強(qiáng)行占有。(鎖的基本特點(diǎn))
  • 請求和保持:“吃著碗里的,想著鍋里的”,即當(dāng)資源請求者在請求其他資源的同時保持原有資源的占有。
  • 循環(huán)等待:即存在一個等待隊(duì)列:P1占有P2的資源,P2占有P3的資源,P3占有P1的資源,這樣就形成了一個等待環(huán)路。舉一個例子:“家門鑰匙鎖車?yán)锪耍囪€匙鎖子在家里了”。

注意:上述的死鎖的條件缺一不可?。。?/strong>

死鎖是一個比較嚴(yán)重的bug,那么在實(shí)踐中我們該如何避免出現(xiàn)死鎖呢?

一個簡單那有效的方法就是破解循環(huán)等待的這個條件。我們可以針對鎖進(jìn)行編號,如果需要同時獲取多把鎖,約定加鎖的順序,務(wù)必是先對小的編號加鎖,后對大的編號加鎖。

比如上述哲學(xué)家就餐問題,我們可以對筷子編號,然后讓哲學(xué)家每次的取的時候先取編號較小的,然后在取編號大的。

如下所示:

多線程(JavaEE初階系列7),JavaEE初階,java-ee,java

如果從5號滑稽開始拿筷子,此時5號滑稽先拿起4號筷子,然后再拿起5號筷子就餐,然后4號滑稽拿起3號筷子,3號滑稽拿起2號筷子,2號滑稽拿起1號筷子,此時1號滑稽要想拿起筷子就需要等到2號滑稽就餐完釋放筷子之后在拿起筷子就餐,所以此時1號滑稽就得阻塞等待。此時就不會造成死鎖了。?

結(jié)束語:

這節(jié)中小編主要是與大家分享了多線程中的最后一些知識點(diǎn),可能有些還沒有給大家講解清楚,不影響在后期的學(xué)習(xí)中小編會一一給大家交代的,希望這節(jié)對大家深入了解多線程有一定幫助,想要學(xué)習(xí)的同學(xué)記得關(guān)注小編和小編一起學(xué)習(xí)吧!如果文章中有任何錯誤也歡迎各位大佬及時為小編指點(diǎn)迷津(在此小編先謝過各位大佬啦?。?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-624912.html

到了這里,關(guān)于多線程(JavaEE初階系列7)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【Java EE初階八】多線程案例(計時器模型)

    【Java EE初階八】多線程案例(計時器模型)

    ????????計時器類似鬧鐘,有定時的功能,其主要是到時間就會執(zhí)行某一操作,即可以指定時間,去執(zhí)行某一邏輯(某一代碼)。 ????????在java標(biāo)準(zhǔn)庫中,提供了Timer類,Timer類的核心方法是schedule( 里面包含兩個參數(shù),一個是要執(zhí)行的任務(wù)代碼,一個是設(shè)置多久之后

    2024年01月21日
    瀏覽(22)
  • JavaEE初階:Java線程的狀態(tài)

    JavaEE初階:Java線程的狀態(tài)

    目錄 獲取當(dāng)前線程引用 休眠當(dāng)前線程 ?線程的狀態(tài) 1.NEW? ? ? ? ? ?? ? 2.TERMINATED? 3.RUNNABLE 4.WAITING 5.TIMED_WAITING 6.BLOCKED 多線程的意義 單線程 ?多線程 這個方法返回當(dāng)前線程的引用。但是我們會對static有疑惑,這其實(shí)是一個靜態(tài)方法,更好的說法是這是一個 類方法, 調(diào)用這

    2024年02月11日
    瀏覽(24)
  • 【JavaEE初階】 線程安全

    【JavaEE初階】 線程安全

    線程安全是多線程編程是的計算機(jī)程序代碼中的一個概念。在擁有共享數(shù)據(jù)的多條線程并行執(zhí)行的程序中,線程安全的代碼會通過同步機(jī)制保證各個線程都可以正常且準(zhǔn)確的執(zhí)行,不會出現(xiàn)數(shù)據(jù)污染等意外情況。上述是百度百科給出的一個概念解釋。換言之,線程安全就是某

    2024年02月08日
    瀏覽(24)
  • javaee初階———多線程(三)

    javaee初階———多線程(三)

    T04BF ??專欄: 算法|JAVA|MySQL|C語言 ?? 小比特 大夢想 此篇文章與大家分享多線程專題第三篇,關(guān)于 線程安全 方面的內(nèi)容 如果有不足的或者錯誤的請您指出! 我們在前面說過,線程之間是搶占式執(zhí)行的,這樣產(chǎn)生的隨機(jī)性,使得程序的執(zhí)行順序變得不一致,就會使得程序產(chǎn)生不同的結(jié)

    2024年04月16日
    瀏覽(36)
  • JavaEE初階:多線程 - 編程

    JavaEE初階:多線程 - 編程

    我們在之前認(rèn)識了什么是多進(jìn)程,今天我們來了解線程。 一個線程就是一個 \\\"執(zhí)行流\\\". 每個線程之間都可以按照順訊執(zhí)行自己的代碼. 多個線程之間 \\\"同時\\\" 執(zhí)行 著多份代碼. 引入 進(jìn)程 這個概念,主要是為了解決并發(fā)編程這樣的問題。因?yàn)閏pu進(jìn)入了多核心的時代,要想進(jìn)一步

    2024年02月12日
    瀏覽(20)
  • 【JavaEE初階】線程的概念與創(chuàng)建

    【JavaEE初階】線程的概念與創(chuàng)建

    本節(jié)目標(biāo) 認(rèn)識多線程 創(chuàng)建多線程 Java 給多線程編程提供了內(nèi)置的支持。 一條線程指的是進(jìn)程中一個單一順序的控制流,一個進(jìn)程中可以并發(fā)多個線程,每條線程并行執(zhí)行不同的任務(wù)。 一個線程就是一個 “執(zhí)行流” . 每個線程之間都可以按照順訊執(zhí)行自己的代碼. 多個線程之

    2024年02月07日
    瀏覽(27)
  • 【JavaEE初階】 線程安全的集合類

    【JavaEE初階】 線程安全的集合類

    原來的集合類, 大部分都不是線程安全的. Vector, Stack, HashTable, 是線程安全的(不建議用), 其他的集合類不是線程安全的. 為什么不建議使用呢? 因?yàn)槲覀冊谑褂玫臅r候,這些類就會自動的加鎖,雖然編譯器會自動優(yōu)化為沒有鎖競爭的線程進(jìn)行鎖消除的優(yōu)化,但是呢萬一編譯器沒

    2024年02月08日
    瀏覽(58)
  • 【JavaEE初階】 線程池詳解與實(shí)現(xiàn)

    【JavaEE初階】 線程池詳解與實(shí)現(xiàn)

    線程池,是一種線程的使用模式,它為了降低線程使用中頻繁的創(chuàng)建和銷毀所帶來的資源消耗與代價。 通過創(chuàng)建一定數(shù)量的線程,讓他們時刻準(zhǔn)備就緒等待新任務(wù)的到達(dá),而任務(wù)執(zhí)行結(jié)束之后再重新回來繼續(xù)待命。 想象這么一個場景: 在學(xué)校附近新開了一家快遞店,老板很

    2024年02月06日
    瀏覽(20)
  • JavaEE初階Day 6:多線程(4)

    前序 :針對Day 5結(jié)尾的count++ 多線程的執(zhí)行,是 隨機(jī)調(diào)度搶占式 的執(zhí)行模式,某個線程執(zhí)行指令過程中,當(dāng)它執(zhí)行到任何一個指令的時候,都有可能被其他線程把它的CPU搶占走 實(shí)際并發(fā)執(zhí)行,由于 上述原因 以及 count++本質(zhì)是CPU的三個指令 ,兩個線程執(zhí)行指令的相對順序就可

    2024年04月14日
    瀏覽(26)
  • 【JavaEE初階】多線程(四)阻塞隊(duì)列 定時器 線程池

    【JavaEE初階】多線程(四)阻塞隊(duì)列 定時器 線程池

    概念 阻塞隊(duì)列是一種特殊的隊(duì)列. 也遵守 “ 先進(jìn)先出 ” 的原則. 阻塞隊(duì)列能是一種線程安全的數(shù)據(jù)結(jié)構(gòu), 并且具有以下特性: 當(dāng)隊(duì)列滿的時候, 繼續(xù)入隊(duì)列就會阻塞, 直到有其他線程從隊(duì)列中取走元素. 當(dāng)隊(duì)列空的時候, 繼續(xù)出隊(duì)列也會阻塞,直到有其他線程往隊(duì)列中插入元素

    2023年04月26日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包