多線程基礎(chǔ)

線程是什么

首先得從程序,進(jìn)程的概念講起

計(jì)算機(jī)程序是指一組指示計(jì)算機(jī)或其他具有消息處理能力設(shè)備每一步動(dòng)作的指令,通常用某種程序設(shè)計(jì)語言編寫,運(yùn)行于某種目標(biāo)體系結(jié)構(gòu)上

進(jìn)程,是指計(jì)算機(jī)中已運(yùn)行的程序

也就是說,程序本身只是指令、數(shù)據(jù)及其組織形式的描述,進(jìn)程才是程序(那些指令和數(shù)據(jù))的真正運(yùn)行實(shí)例

線程操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)。

線程<進(jìn)程≈程序,一個(gè)進(jìn)程中的多個(gè)線程可以共享相同的內(nèi)存單元

創(chuàng)建方式

在java中,把線程抽象為Thread對象,創(chuàng)建線程有三種方式

  1. 繼承Thread類,重寫run方法
class MyThread extends Thread {
    @Override
    public void run() {
        //方法體
    }
}
  1. 實(shí)現(xiàn)Runnable或Callable接口
class MyThread implements Runnable {
    @Override
    publicvoid run() {
        //方法體
    }
}
class MyThread implements Callable {
    @Override
    public Object call() throws Exception {
        return null;
    }
}

這兩種方式的優(yōu)劣對比:

  • 方式1,線程類已經(jīng)繼承了Thread類,不能再繼承其他類
  • 方式2,線程類只實(shí)現(xiàn)了Runnable接口或者Callable接口,還可以繼承其他類
  • 方式2,多個(gè)線程可以共享一個(gè)target對象,非常適合多個(gè)相同線程來處理同一份資源的情況,從而可以將CPU、代碼和數(shù)據(jù)分開,形成清晰的模型,體現(xiàn)了面向?qū)ο蟮木幊趟枷?/li>

綜合來說,還是推薦使用方式2

3.線程池

即使采用方式2創(chuàng)建線程有很多優(yōu)勢,在實(shí)際生產(chǎn)中還是不推薦使用,最好的創(chuàng)建方式就是--不用自己手動(dòng)創(chuàng)建,而是使用線程池管理線程

相對于手動(dòng)創(chuàng)建線程,線程池的優(yōu)勢在于減少創(chuàng)建新線程的時(shí)間和重復(fù)利用線程池中的線程

傳送門--線程池

線程生命周期

先來看線程的狀態(tài)

JDK文檔上面明確指定,Thread有6種狀態(tài):

  • NEW

尚未啟動(dòng)的線程的線程狀態(tài)

也就是Thread對象剛被創(chuàng)建時(shí)但還未執(zhí)行start()時(shí)的狀態(tài)

  • RUNNABLE

可運(yùn)行線程的線程狀態(tài)。處于可運(yùn)行狀態(tài)的線程正在Java虛擬機(jī)中執(zhí)行,但它可能正在等待操作系統(tǒng)中的其他資源,比如處理器

執(zhí)行了start()方法后的狀態(tài),注意RUNNABLE表示操作系統(tǒng)中定義的線程狀態(tài)中的READY+RUNNNING

  • BLOCKED

等待監(jiān)視器鎖的阻塞線程的線程狀態(tài)。處于阻塞狀態(tài)的線程正在等待監(jiān)視器鎖進(jìn)入同步塊/方法,或者在調(diào)用Object.wait后重新進(jìn)入同步塊/方法

這是jdk文檔的解釋,也就是說線程在等待監(jiān)視器鎖的情況就是阻塞態(tài),或者在調(diào)用Object.wait后重新進(jìn)入同步塊/方法這句話的意思是, 在調(diào)用wait()后,線程狀態(tài)從RUNNABLE->WAITING,當(dāng)被其他線程調(diào)用notify()或notifyAll()喚醒后,先進(jìn)入RUNNABLE狀態(tài),這個(gè)時(shí)候可能拿不到鎖,所以就會(huì)進(jìn)入BLOCKED阻塞態(tài)

  • WAITING

等待線程的線程狀態(tài)。由于調(diào)用以下方法之一,線程處于等待狀態(tài):
Object.wait with no timeout
Thread.join with no timeout
LockSupport.park

WAITING狀態(tài)只能從RUNNABLE狀態(tài)變過來

  • TIMED_WAITING

具有指定等待時(shí)間的等待線程的線程狀態(tài)。線程由于調(diào)用下列方法之一,并指定了正等待時(shí)間,因此處于定時(shí)等待狀態(tài):
Thread.sleep
Object.wait with timeout
Thread.join with timeout
LockSupport.parkNanos
LockSupport.parkUntil

  • TERMINATED

終止線程的線程狀態(tài)。線程已經(jīng)完成執(zhí)行

下面這張圖很好地描述了java線程狀態(tài)之間的轉(zhuǎn)換

線程間的可見性

雖然多個(gè)線程間是共享數(shù)據(jù)的,但不一定一個(gè)線程修改了共享數(shù)據(jù),另一個(gè)線程馬上就能感知,這就需要volatile關(guān)鍵字來保證線程間的可見性,詳情請參考volatile關(guān)鍵字

線程同步機(jī)制

多個(gè)線程并發(fā)訪問共享數(shù)據(jù),必然會(huì)出現(xiàn)數(shù)據(jù)不一致的情況,那么就需要線程同步機(jī)制來保證數(shù)據(jù)一致性

內(nèi)置鎖

內(nèi)置鎖是java從語法級別上支持的一種同步機(jī)制,主要由synchronized關(guān)鍵字實(shí)現(xiàn),有兩種實(shí)現(xiàn)方式:

1.同步代碼塊

實(shí)現(xiàn)方式如下

synchronized(監(jiān)視器對象) {
  ...
}
//常見寫法有兩種
synchronized(this) {
  ...
}
synchronized(this.getClass()) {
  ...
}

監(jiān)視器對象可以是任意對象,但必須是同一個(gè)對象才能達(dá)到同步效果

  1. 同步方法
    同步方法又分為普通同步方法和靜態(tài)同步方法
  • 普通同步方法
public synchronized void method() {
}
//相當(dāng)于
synchronized(this) {
  ...
}
  • 靜態(tài)同步方法
public static synchronized void method() {
}
//相當(dāng)于
synchronized(this.getClass()) {
  ...
}
顯式鎖

顯式鎖是Java5中并發(fā)工具包提供的,詳情請參考線程八鎖

CAS

CAS是一種無鎖算法,類似于樂觀鎖,同時(shí)兼顧并發(fā)性能和數(shù)據(jù)一致性,詳情請參看CAS

死鎖問題

死鎖

線程通信

線程之間靠通信來協(xié)作,主要是靠這三個(gè)方法

  • wait
    使擁有當(dāng)前監(jiān)視器對象的線程進(jìn)入WAITING狀態(tài),并釋放監(jiān)視器所有權(quán),直到同一監(jiān)視器對象調(diào)用notify()或notifyAll()才可以喚醒,重新持有監(jiān)視器,也可以調(diào)用wait(long time)使進(jìn)入TIMED_WAITING,時(shí)間到了就喚醒.被喚醒后進(jìn)入RUNNABLE狀態(tài),很有可能拿不到監(jiān)視器,從而進(jìn)入BLOCKED狀態(tài)

雖然,wait()和Thread.sleep()有著看似相同的效果,都會(huì)讓當(dāng)前線程進(jìn)入WAITING狀態(tài),但注意和Thread.sleep()的區(qū)別:
1.Thread.sleep()不會(huì)釋放監(jiān)視器,而wait()會(huì)釋放監(jiān)視器
2.wait()只能在同步方法或同步代碼塊里調(diào)用,Thread.sleep()可以在任意地方調(diào)用

還有注意可能出現(xiàn)的虛假喚醒問題,這個(gè)方法應(yīng)該總是在循環(huán)中使用

synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
}
  • notify
    喚醒正在此對象監(jiān)視器上等待的單個(gè)線程。如果有任何線程正在等待這個(gè)對象,則選擇其中一個(gè)線程被喚醒。選擇是任意的,由實(shí)現(xiàn)決定.在喚醒該線程后,該線程進(jìn)入RUNNABLE狀態(tài),但因?yàn)樵趩拘褧r(shí),喚醒線程持有監(jiān)視器,所以很有可能等待線程即使被喚醒也拿不到監(jiān)視器,從而進(jìn)入BLOCKED狀態(tài)

  • notifyAll
    喚醒正在此對象監(jiān)視器上等待的所有線程

需要注意的是,這三個(gè)方法都需要在同步方法或同步代碼塊里調(diào)用,因?yàn)檎{(diào)用的是同步監(jiān)視器對象的方法;而且是由相同的監(jiān)視器對象調(diào)用.

juc里的線程通信工具類

juc包提供了線程通信的工具類,開箱即用,傳送門--juc里的線程通信工具類

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 多線程 多線程概念 進(jìn)程:正在進(jìn)行中的程序 線程:是進(jìn)程中一個(gè)負(fù)責(zé)程序執(zhí)行的控制單元(執(zhí)行路徑)一個(gè)進(jìn)行中可以有多...
    聽見下雨的_聲音閱讀 245評論 0 0
  • 標(biāo)簽(空格分隔): java java 5新增機(jī)制--->同步鎖(Lock) 從java5開始,java提供了一種...
    Sivin閱讀 631評論 1 5
  • 一、線程基本概念 1. 線程的五種狀態(tài) 新建狀態(tài)(new): 線程對象被創(chuàng)建后,就進(jìn)入了新建狀態(tài)。例如,Threa...
    Lynn_R01612x2閱讀 478評論 0 1
  • 進(jìn)程:正在執(zhí)行中的程序,其實(shí)是應(yīng)用程序在內(nèi)存中運(yùn)行的那片空間。 線程:進(jìn)程中的一個(gè)執(zhí)行單元,負(fù)責(zé)進(jìn)程中程序的執(zhí)行。...
    七弦桐語閱讀 549評論 2 7
  • 概要本章,會(huì)對線程等待/喚醒方法進(jìn)行介紹。涉及到的內(nèi)容包括: wait(), notify(), notifyAl...
    博格體閱讀 375評論 0 0

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