線程相關(guān)的知識(shí)歸納整理

CPU的核心數(shù)和線程數(shù)的關(guān)系

CPU的核心數(shù)和線程數(shù)一般是1:1的關(guān)系,Intel推出的超線程技術(shù)能使電腦的核心數(shù)和線程數(shù)的比達(dá)到 1:2 (基于一個(gè)物理核心模擬兩個(gè)邏輯核心),即是一個(gè)4核的CPU同時(shí)可運(yùn)行4個(gè)線程,如果使用了超線程技術(shù)就可以同時(shí)運(yùn)行8個(gè)線程,linux 系統(tǒng)下一個(gè)進(jìn)程最大創(chuàng)建1000個(gè)線程,windows系統(tǒng)下一個(gè)進(jìn)程最大創(chuàng)建2000個(gè)線程)

CPU時(shí)間片輪轉(zhuǎn)機(jī)制(RR調(diào)度)

操作系統(tǒng)把所有就緒進(jìn)程按先入先出的原則排成一個(gè)隊(duì)列。新來的進(jìn)程加到就緒隊(duì)列末尾。每當(dāng)執(zhí)行進(jìn)程調(diào)度時(shí),進(jìn)程調(diào)度程序總是選出就緒隊(duì)列的隊(duì)首進(jìn)程,讓它在CPU上運(yùn)行一個(gè)時(shí)間片的時(shí)間。時(shí)間片是一個(gè)很小的時(shí)間單位,通常為10~100ms數(shù)量級(jí)。當(dāng)進(jìn)程用完分給它的時(shí)間片后,系統(tǒng)的計(jì)時(shí)器發(fā)出時(shí)鐘中斷,調(diào)度程序便停止該進(jìn)程的運(yùn)行,把它放入就緒隊(duì)列的末尾;然后把CPU分給就緒隊(duì)列的隊(duì)首進(jìn)程,同樣也讓它運(yùn)行一個(gè)時(shí)間片,如此往復(fù)。(當(dāng)前時(shí)間片執(zhí)行完成后,線程還未完成,就會(huì)保存資源,切換到下一個(gè)時(shí)間片 執(zhí)行下一個(gè)線程,這個(gè)過程叫上下文切換 大概消耗20000個(gè)cpu時(shí)間周期,一個(gè)cpu時(shí)間周期大約為 執(zhí)行一個(gè)1+1 操作)

進(jìn)程和線程的定義和區(qū)別

  • 進(jìn)程進(jìn)程是程序運(yùn)行資源分配的最小單位
    進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的最小單位,其中資源包括:CPU、內(nèi)存空間、磁盤等,同一進(jìn)程中的多條線程共享該進(jìn)程中的全部系統(tǒng)資源,而進(jìn)程和進(jìn)程之間是相互獨(dú)立的。
  • 進(jìn)程是具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動(dòng)
  • 進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。
    進(jìn)程是程序在計(jì)算機(jī)上的一次執(zhí)行活動(dòng)。當(dāng)你運(yùn)行一個(gè)程序,你就啟動(dòng)了一個(gè)進(jìn)程。顯然,程序是死的、靜態(tài)的,進(jìn)程是活的、動(dòng)態(tài)的。進(jìn)程可以分為系統(tǒng)進(jìn)程和用戶進(jìn)程。凡是用于完成操作系統(tǒng)的各種功能的進(jìn)程就是系統(tǒng)進(jìn)程,它們就是處于運(yùn)行狀態(tài)下的操作系統(tǒng)本身,用戶進(jìn)程就是所有由你啟動(dòng)的進(jìn)程。
  • 線程線程是CPU調(diào)度的最小單位,必須依賴于進(jìn)程而存在
    線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的、能獨(dú)立運(yùn)行的基本單位。線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器,一組寄存器和棧),但是它可與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源。一個(gè)進(jìn)程中有一個(gè)線程存活,那么進(jìn)程就是存活的,因?yàn)榫€程共享它所依賴的進(jìn)程的資源.

并行和并發(fā)

  • 并行:以一個(gè)具有超線程的4核CPU而言,最大可并行8個(gè)線程。
  • 并發(fā):以一個(gè)具有超線程的4核CPU而言,在1秒鐘內(nèi)可執(zhí)行100個(gè)時(shí)間片,并發(fā) 能力就是100,并發(fā)必須依賴于單位時(shí)間,否則沒有意義。
  • 二者區(qū)別:一個(gè)是同時(shí)執(zhí)行,一個(gè)是交替執(zhí)行

高并發(fā)編程的意義,好處和注意事項(xiàng)(java里的程序天生就是多線程的)

  • 意義:更充分的利用cpu資源(特別是多核cpu)
  • 好處:加快響應(yīng)用戶的時(shí)間,能讓程序執(zhí)行的更快,也能讓代碼模塊化,異步化,簡單化。
  • 注意事項(xiàng)
    1)程安全性,主要是多個(gè)線程共享數(shù)據(jù)時(shí)可能會(huì)產(chǎn)生于期望不相符的結(jié)果
    2)線程之間的死循環(huán),為了解決線程之間的安全性引入了Java的鎖機(jī)制,而一不小心就會(huì)產(chǎn)生Java線程死鎖的多線程問題,因?yàn)椴煌木€程都在等待那些根本不可能被釋放的鎖,從而導(dǎo)致所有的工作都無法完成。
    3)線程過多時(shí)會(huì)使得CPU頻繁切換,花在調(diào)度上時(shí)間太多,可能造成死機(jī)宕機(jī)等問題。
    4)線程過多還會(huì)消耗過多內(nèi)存。(一個(gè)線程基本內(nèi)存配置為1M)

認(rèn)識(shí)Java里的線程

  • Java里的程序天生就是多線程的,JDK中唯一能代表線程的就是Thread, Runnable和Callable只是對(duì)任務(wù)的一個(gè)抽象接口,不是線程。
  • 一個(gè)Java程序從main()方法開始執(zhí)行,然后按照既定的代碼邏輯執(zhí)行,看似沒有其他線程參與,但實(shí)際上Java程序天生就是多線程程序,因?yàn)閳?zhí)行main()方法的是一個(gè)名稱為main的線程。
    [6] Monitor Ctrl-Break //監(jiān)控Ctrl-Break中斷信號(hào)的
    [5] Attach Listener //內(nèi)存dump,線程dump,類信息統(tǒng)計(jì),獲取系統(tǒng)屬性等
    [4] Signal Dispatcher // 分發(fā)處理發(fā)送給JVM信號(hào)的線程
    [3] Finalizer // 調(diào)用對(duì)象finalize方法的線程
    [2] Reference Handler//清除Reference的線程
    [1] main //main線程,用戶程序入口

線程的生命周期圖

線程的聲明周期

線程的使用

  • 通過繼承實(shí)現(xiàn)一個(gè)線程


    image.png
  • 通過接口實(shí)現(xiàn)


    image.png
  • 不同實(shí)現(xiàn)的使用


    image.png
  • 一個(gè)線程Thread對(duì)象,只能執(zhí)行一次start()方法。
  • run()方法和start()方法的區(qū)別:
    1)run()方法只是一個(gè)普通方法,主要是業(yè)務(wù)邏輯實(shí)現(xiàn)的地方
    2)start()方法才是將thread和系統(tǒng)的線程關(guān)聯(lián)并執(zhí)行的方法,執(zhí)行start()才是一個(gè)線程創(chuàng)建啟動(dòng)的標(biāo)志。

線程的停止方式

  • 一個(gè)線程在不干預(yù)的情況下,執(zhí)行完成run方法中的代碼就會(huì)停止。
  • 已過時(shí)的停止線程的方法:
    stop(),destroy() 停止過于粗暴,調(diào)用該方法會(huì)立馬殺死線程,可能資源沒有被釋放,容易造成資源泄漏
    suspend() 掛起線程的方法(容易造成死鎖所以被棄用了)
    resume() 將掛起線程恢復(fù)。
  • 目前可使用的停止線程的方法,interrupt() 打斷線程,可能不能正常中斷。需要使用 interrupted() 和 isInterrupted()來判斷。使用interrupt中斷線程 需要在線程中判斷interrupted標(biāo)志自己中斷。(interrupted() 判斷一次后 第二次 interrupt的狀態(tài)會(huì)變成false)


    image.png
  • 如果一個(gè)線程處于了阻塞狀態(tài)(如線程調(diào)用了thread.sleep、thread.join、thread.wait、),則在線程在檢查中斷標(biāo)示時(shí)如果發(fā)現(xiàn)中斷標(biāo)示為true,則會(huì)在這些阻塞方法調(diào)用處拋出InterruptedException異常,并且在拋出異常后會(huì)立即將線程的中斷標(biāo)示位清除,即重新設(shè)置為false。
    不建議自定義一個(gè)取消標(biāo)志位來中止線程的運(yùn)行。因?yàn)閞un方法里有阻塞調(diào)用時(shí)會(huì)無法很快檢測(cè)到取消標(biāo)志,線程必須從阻塞調(diào)用返回后,才會(huì)檢查這個(gè)取消標(biāo)志。這種情況下,使用中斷會(huì)更好,因?yàn)椋?、一般的阻塞方法,如sleep等本身就支持中斷的檢查,二、檢查中斷位的狀態(tài)和檢查取消標(biāo)志位沒什么區(qū)別,用中斷位的狀態(tài)還可以避免聲明取消標(biāo)志位,減少資源的消耗。
  • 注意:處于死鎖狀態(tài)的線程無法被中斷

深入理解run()和start()

Thread類是Java里對(duì)線程概念的抽象,可以這樣理解:我們通過new Thread()其實(shí)只是new出一個(gè)Thread的實(shí)例,還沒有操作系統(tǒng)中真正的線程掛起鉤來。只有執(zhí)行了start()方法后,才實(shí)現(xiàn)了真正意義上的啟動(dòng)線程。start()方法讓一個(gè)線程進(jìn)入就緒隊(duì)列等待分配cpu,分到cpu后才調(diào)用實(shí)現(xiàn)的run()方法,start()方法不能重復(fù)調(diào)用。而run方法是業(yè)務(wù)邏輯實(shí)現(xiàn)的地方,本質(zhì)上和任意一個(gè)類的任意一個(gè)成員方法并沒有任何區(qū)別,可以重復(fù)執(zhí)行,可以被單獨(dú)調(diào)用。

其他的線程方法

  • join()方法:可以實(shí)現(xiàn)讓線程順序執(zhí)行,普通情況下執(zhí)行兩個(gè)線程,線程都是交替執(zhí)行 時(shí)間片輪轉(zhuǎn)機(jī)制(RR調(diào)度)
    image.png

    使用join()方法使得線程2在線程1執(zhí)行完成后執(zhí)行:在線程2中傳入線程1,并且線程1調(diào)用join()方法進(jìn)行插隊(duì),最后運(yùn)行結(jié)果是線程1先執(zhí)行完畢再執(zhí)行線程2.
    image.png
  • yield()方法:使當(dāng)前線程讓出CPU占有權(quán),但讓出的時(shí)間是不可設(shè)定的。也不會(huì)釋放鎖資源,所有執(zhí)行yield()的線程有可能在進(jìn)入到可執(zhí)行狀態(tài)后馬上又被執(zhí)行。
  • 等待/通知機(jī)制
    是指一個(gè)線程A調(diào)用了對(duì)象O的wait()方法進(jìn)入等待狀態(tài),而另一個(gè)線程B調(diào)用了對(duì)象O的notify()或者notifyAll()方法,線程A收到通知后從對(duì)象O的wait()方法返回,進(jìn)而執(zhí)行后續(xù)操作。上述兩個(gè)線程通過對(duì)象O來完成交互,而對(duì)象上的wait()和notify/notifyAll()的關(guān)系就如同開關(guān)信號(hào)一樣,用來完成等待方和通知方之間的交互工作。
    notify():通知一個(gè)在對(duì)象上等待的線程,使其從wait方法返回,而返回的前提是該線程獲取到了對(duì)象的鎖,沒有獲得鎖的線程重新進(jìn)入WAITING狀態(tài)。
    notifyAll():通知所有等待在該對(duì)象上的線程
    wait():調(diào)用該方法的線程進(jìn)入 WAITING狀態(tài),只有等待另外線程的通知或被中斷才會(huì)返回.需要注意,調(diào)用wait()方法后,會(huì)釋放對(duì)象的鎖
    wait(long):超時(shí)等待一段時(shí)間,這里的參數(shù)時(shí)間是毫秒,也就是等待長達(dá)n毫秒,如果沒有通知就超時(shí)返回
    wait (long,int):對(duì)于超時(shí)時(shí)間更細(xì)粒度的控制,可以達(dá)到納秒。
    等待和通知方的使用標(biāo)準(zhǔn)范式:
    等待方的書寫范式
    通知方的書寫范式

線程間的共享

線程開始運(yùn)行,擁有自己的??臻g,就如同一個(gè)腳本一樣,按照既定的代碼一步一步地執(zhí)行,直到終止。但是,每個(gè)運(yùn)行中的線程,如果僅僅是孤立地運(yùn)行,那么沒有一點(diǎn)兒價(jià)值,或者說價(jià)值很少,如果多個(gè)線程能夠相互配合完成工作,包括數(shù)據(jù)之間的共享,協(xié)同處理事情。這將會(huì)帶來巨大的價(jià)值。
Java支持多個(gè)線程同時(shí)訪問一個(gè)對(duì)象或者對(duì)象的成員變量,關(guān)鍵字synchronized可以修飾方法或者以同步塊的形式來進(jìn)行使用,它主要確保多個(gè)線程在同一個(gè)時(shí)刻,只能有一個(gè)線程處于方法或者同步塊中,它保證了線程對(duì)變量訪問的可見性和排他性,又稱為內(nèi)置鎖機(jī)制。

  • 對(duì)象鎖和類鎖:
    對(duì)象鎖是用于對(duì)象實(shí)例方法,或者一個(gè)對(duì)象實(shí)例上的,類鎖是用于類的靜態(tài)方法或者一個(gè)類的class對(duì)象上的。我們知道,類的對(duì)象實(shí)例可以有很多個(gè),但是每個(gè)類只有一個(gè)class對(duì)象,所以不同對(duì)象實(shí)例的對(duì)象鎖是互不干擾的,但是每個(gè)類只有一個(gè)類鎖。
    但是有一點(diǎn)必須注意的是,其實(shí)類鎖只是一個(gè)概念上的東西,并不是真實(shí)存在的,類鎖其實(shí)鎖的是每個(gè)類的對(duì)應(yīng)的class對(duì)象。類鎖和對(duì)象鎖之間也是互不干擾的。

線程間的協(xié)作

線程之間相互配合,完成某項(xiàng)工作,比如:一個(gè)線程修改了一個(gè)對(duì)象的值,而另一個(gè)線程感知到了變化,然后進(jìn)行相應(yīng)的操作,整個(gè)過程開始于一個(gè)線程,而最終執(zhí)行又是另一個(gè)線程。前者是生產(chǎn)者,后者就是消費(fèi)者,這種模式隔離了“做什么”(what)和“怎么做”(How),簡單的辦法是讓消費(fèi)者線程不斷地循環(huán)檢查變量是否符合預(yù)期在while循環(huán)中設(shè)置不滿足的條件,如果條件滿足則退出while循環(huán),從而完成消費(fèi)者的工作。卻存在如下問題:
1)難以確保及時(shí)性。
2)難以降低開銷。如果降低睡眠的時(shí)間,比如休眠1毫秒,這樣消費(fèi)者能更加迅速地發(fā)現(xiàn)條件變化,但是卻可能消耗更多的處理器資源,造成了無端的浪費(fèi)。

ThreadLocal

ThreadLocal,即線程變量,是一個(gè)以ThreadLocal對(duì)象為鍵、任意對(duì)象為值的存儲(chǔ)結(jié)構(gòu)。這個(gè)結(jié)構(gòu)被附帶在線程上,也就是說一個(gè)線程可以根據(jù)一個(gè)ThreadLocal對(duì)象查詢到綁定在這個(gè)線程上的一個(gè)值, ThreadLocal往往用來實(shí)現(xiàn)變量在線程之間的隔離。threadLocal類接口很簡單,只有4個(gè)方法,我們先來了解一下:
? void set(Object value)
設(shè)置當(dāng)前線程的線程局部變量的值。
? public Object get()
該方法返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量。
? public void remove()
將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對(duì)應(yīng)該線程的局部變量將自動(dòng)被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。
? protected Object initialValue()
返回該線程局部變量的初始值,該方法是一個(gè)protected的方法,顯然是為了讓子類覆蓋而設(shè)計(jì)的。這個(gè)方法是一個(gè)延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null。

public final static ThreadLocal<String> RESOURCE = new ThreadLocal<String>(){
    @Override
    protected String initialValue(){
        return “string的初始值”;
    }
};

RESOURCE代表一個(gè)能夠存放String類型的ThreadLocal對(duì)象。此時(shí)不論什么一個(gè)線程能夠并發(fā)訪問這個(gè)變量,對(duì)它進(jìn)行寫入、讀取操作,都是線程安全的。

顯式鎖

  • Lock接口和synchronized的比較
    我們一般的Java程序是靠synchronized關(guān)鍵字實(shí)現(xiàn)鎖功能的,使用synchronized關(guān)鍵字將會(huì)隱式地獲取鎖,但是它將鎖的獲取和釋放固化了,也就是先獲取再釋放。synchronized屬于Java語言層面的鎖,也被稱之為內(nèi)置鎖。synchronized這種機(jī)制,一旦開始獲取鎖,是不能中斷的,也不提供嘗試獲取鎖的機(jī)制。
    而Lock是由Java在語法層面提供的,鎖的獲取和釋放需要我們明顯的去獲取,因此被稱為顯式鎖。并且提供了synchronized不提供的機(jī)制。
    Lock的特性描述

Lock接口和核心方法

在finally塊中釋放鎖,目的是保證在獲取到鎖之后,最終能夠被釋放。

  private Lock lock = new ReentrantLock();
    public void lockUse(){
        lock.lock();
        try {
            //執(zhí)行業(yè)務(wù)代碼
            goodsInfo.changeNumber(60);
        }finally {
            //使用try finally 是為了保證即使業(yè)務(wù)代碼報(bào)錯(cuò) 也能釋放鎖
            lock.unlock();
        }
    }
lock的方法解析

可重入鎖ReentrantLock、所謂鎖的公平和非公平

  • 可重入鎖
    synchronized關(guān)鍵字隱式的支持重進(jìn)入,比如一個(gè)synchronized修飾的遞歸方法,在方法執(zhí)行時(shí),執(zhí)行線程在獲取了鎖之后仍能連續(xù)多次地獲得該鎖。ReentrantLock在調(diào)用lock()方法時(shí),已經(jīng)獲取到鎖的線程,能夠再次調(diào)用lock()方法獲取鎖而不被阻塞。
  • 公平鎖和非公平鎖
    如果在時(shí)間上,先對(duì)鎖進(jìn)行獲取的請(qǐng)求一定先被滿足,那么這個(gè)鎖是公平的,反之,是不公平的。公平的獲取鎖,也就是等待時(shí)間最長的線程最優(yōu)先獲取鎖,也可以說鎖獲取是順序的。
    ReentrantLock提供了一個(gè)構(gòu)造函數(shù),能夠控制鎖是否是公平的。事實(shí)上,公平的鎖機(jī)制往往沒有非公平的效率高。原因是,在恢復(fù)一個(gè)被掛起的線程與該線程真正開始運(yùn)行之間存在著嚴(yán)重的延遲。假設(shè)線程A持有一個(gè)鎖,并且線程B請(qǐng)求這個(gè)鎖。由于這個(gè)鎖已被線程A持有,因此B將被掛起。當(dāng)A釋放鎖時(shí),B將被喚醒,因此會(huì)再次嘗試獲取鎖。與此同時(shí),如果C也請(qǐng)求這個(gè)鎖,那么C很可能會(huì)在B被完全喚醒之前獲得、使用以及釋放這個(gè)鎖。這樣的情況是一種“雙贏”的局面:B獲得鎖的時(shí)刻并沒有推遲,C更早地獲得了鎖,并且吞吐量也獲得了提高。

讀寫鎖ReentrantReadWriteLock

之前提到鎖(synchronized和ReentrantLock)基本都是排他鎖,這些鎖在同一時(shí)刻只允許一個(gè)線程進(jìn)行訪問,而讀寫鎖在同一時(shí)刻可以允許多個(gè)讀線程訪問,但是在寫線程訪問時(shí),所有的讀線程和其他寫線程均被阻塞。讀寫鎖維護(hù)了一對(duì)鎖,一個(gè)讀鎖和一個(gè)寫鎖,通過分離讀鎖和寫鎖,使得并發(fā)性相比一般的排他鎖有了很大提升。
除了保證寫操作對(duì)讀操作的可見性以及并發(fā)性的提升之外,讀寫鎖能夠簡化讀寫交互場景的編程方式。假設(shè)在程序中定義一個(gè)共享的用作緩存數(shù)據(jù)結(jié)構(gòu),它大部分時(shí)間提供讀服務(wù)(例如查詢和搜索),而寫操作占有的時(shí)間很少,但是寫操作完成之后的更新需要對(duì)后續(xù)的讀服務(wù)可見。
一般情況下,讀寫鎖的性能都會(huì)比排它鎖好,因?yàn)榇蠖鄶?shù)場景讀是多于寫的。在讀多于寫的情況下,讀寫鎖能夠提供比排它鎖更好的并發(fā)性和吞吐量

import java.util.concurrent.locks.*;
public class UsWrLock implements GoodsService {
    private GoodsInfo goodsInfo;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock write = lock.writeLock();
    private final Lock read = lock.readLock();
    @Override
    public GoodsInfo getGoodsInfo() {
        read.lock();
        try {
            //讀操作 返回商品信息
            SleepTools.ms(5);
            return this.goodsInfo;
        }finally { read.unlock(); }
    }

    @Override
    public void sellGoods(int sellNumber) {
        write.lock();
        try {
            //寫操作,改變商品信息
            SleepTools.ms(5);
            goodsInfo.changeNumber(sellNumber);
        }finally { write.unlock(); }
    }

    public UsWrLock(GoodsInfo goodsInfo) {
        this.goodsInfo = goodsInfo;
    }
}

Condition接口

任意一個(gè)Java對(duì)象,都擁有一組監(jiān)視器方法(定義在java.lang.Object上),主要包括wait()、wait(long timeout)、notify()以及notifyAll()方法,這些方法與synchronized同步關(guān)鍵字配合,可以實(shí)現(xiàn)等待/通知模式。Condition接口也提供了類似Object的監(jiān)視器方法,與Lock配合可以實(shí)現(xiàn)等待/通知模式。
用Lock和Condition實(shí)現(xiàn)等待通知, condition.signal()只能隨機(jī)喚醒一個(gè)等待線程,condition.signalAll()喚醒所有等待線程。如下是一份使用condition實(shí)現(xiàn)的等待通知模式的代碼:

public class ExpressCond {
    public final static String CITY = "ChengDu";
    private int km;/*快遞運(yùn)輸里程數(shù)*/
    private String site;/*快遞到達(dá)地點(diǎn)*/
    private Lock lock = new ReentrantLock();
    private Condition siteCond = lock.newCondition();
    private Condition kmCond = lock.newCondition();

    public ExpressCond(int km, String site) {
        this.km = km;
        this.site = site;
    }

    /* 變化公里數(shù),然后通知處于wait狀態(tài)并需要處理公里數(shù)的線程進(jìn)行業(yè)務(wù)處理*/
    public void changeKm(int mileage){
        lock.lock();
        try {
            km = mileage;
            kmCond.signalAll();
        }finally {
            lock.unlock();
        }
    }

    /* 變化地點(diǎn),然后通知處于wait狀態(tài)并需要處理地點(diǎn)的線程進(jìn)行業(yè)務(wù)處理*/
    public  void changeSite(String cityName){
        lock.lock();
        try {
            this.site = cityName;
            siteCond.signalAll();
        }finally {
            lock.unlock();
        }
    }

    /*當(dāng)快遞的里程數(shù)大于100時(shí)更新數(shù)據(jù)庫*/
    public void waitKm(){
        //TODO
        lock.lock();
        try {
            while (km < 100){
                try {
                    kmCond.await();
                    System.out.println("check KM thread["+Thread.currentThread().getId() +"] is be notifed.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }finally {
            lock.unlock();
        }
        System.out.println("the Km is "+this.km+",I will change db");
    }

    /*當(dāng)快遞到達(dá)目的地時(shí)通知用戶*/
    public void waitSite(){
        lock.lock();
        try {
            while(CITY.equals(this.site)) {
                try {
                    siteCond.await();
                    System.out.println("check Site thread["+Thread.currentThread().getId() +"] is be notifed.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }finally {
            lock.unlock();
        }
        System.out.println("the site is "+this.site+",I will call user");
    }
}

  • 使用
public class TestCond {
    private static ExpressCond express = new ExpressCond(0,ExpressCond.CITY);

    /*檢查里程數(shù)變化的線程,不滿足條件,線程一直等待*/
    private static class CheckKm extends Thread{
        @Override
        public void run() {
            express.waitKm();
        }
    }

    /*檢查地點(diǎn)變化的線程,不滿足條件,線程一直等待*/
    private static class CheckSite extends Thread{
        @Override
        public void run() {
            express.waitSite();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<3;i++){
            new CheckSite().start();
        }
        for(int i=0;i<3;i++){
            new CheckKm().start();
        }

        Thread.sleep(1000);
        express.changeKm(102);//快遞里程變化
        express.changeSite("北京天安門");//快遞里程變化
    }
}
  • 結(jié)果
    condition使用結(jié)果
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容