Thread 小总结
1.線程概述
線程是一個程序的多個執(zhí)行路徑,執(zhí)行調(diào)度的單元,依托于進(jìn)程的存在。線不僅可以共享進(jìn)程的內(nèi)在,而且還擁有一個屬于自己的內(nèi)存空間,這段內(nèi)存空間就是線程棧,是在建立線程時系統(tǒng)分配的,主要用來保存線內(nèi)部所使用的數(shù)據(jù),如線程執(zhí)行函數(shù)中所定義的變量,線程的狀態(tài)等信息。
Java中的多線程是一個搶占機制,搶占機制指的是多個線程處于可運行狀態(tài),但只是允許一個線程運行,他們通過競爭方式搶占CPU。
2.線程的定義
定義一個線程(Defining a Thread)有兩種方法:
[1] 繼承java.lang.Thread類,重寫run()方法?
/** * 使用繼承java.lang.Thread類的方式創(chuàng)建一個線程 * * @author DreamSea 2011-12-29 20:17:06 */ public class ThreadTest extends Thread { /** * 重寫(Override)run()方法 JVM會自動調(diào)用該方法 */ public void run() { System.out.println("I'm running!"); } }重寫(override)run()方法在該線程的start()方法被調(diào)用后,JVM會自動調(diào)用run方法來執(zhí)行任務(wù)。
?
[2] 實現(xiàn)java.lang.Runnable接口,并將其傳入到Thread類對象中
/** * 通過實現(xiàn)Runnable接口創(chuàng)建一個線程 * @author DreamSea */ public class ThreadTest implements Runnable { public void run() { System.out.println("I'm running!"); } }
3.線程的啟動
任何一個線程的執(zhí)行的前提都是必須有Thread class的實例存在,并且通過調(diào)用run()方法啟動線程。 [1] 如果線程是繼承Thread類,則創(chuàng)建方式如下: ThreadTest1 thread1 = new ThreadTest1(); thread1.start();[2] 如果是實現(xiàn)Runnable接口,則創(chuàng)建方式如下:
ThreadTest2 thread2 = new ThreadTest2(); Thread t = new Thread(thread2); t.start();同步的前提
1. 必須要有2個或者2個以上的線程
2. 必須是多個線程使用同一個鎖,多個線程可以同時操作同一個鎖下的代碼。
同步的弊端
1. 線程每次進(jìn)入同步代碼塊或同步函數(shù)都要判斷鎖,浪費資源,影響效率
2. 可能出現(xiàn)死鎖現(xiàn)象,多發(fā)生在一個同步代碼塊或同步函數(shù)中嵌套另一個同步函數(shù)或同步代碼塊,且2個同步上使用不同的鎖。即同步中嵌套同步而鎖不同就容易引發(fā)死鎖。
4.線程的狀態(tài)
? [1] 新生狀態(tài)(New):當(dāng)一個線程的實例被創(chuàng)建即使用new關(guān)鍵字和Thread類或其子類創(chuàng)建一個線程對象后,此時該線程處于新生(new)狀態(tài),處于新生狀態(tài)的線程有自己的內(nèi)存空間,但該線程并沒有運行,此時線程還不是活著的(not alive)。[2] 就緒狀態(tài)(Runnable):通過調(diào)用線程實例的start()方法來啟動線程使線程進(jìn)入就緒狀態(tài)(runnable);處于就緒狀態(tài)的線程已經(jīng)具備了運行條件,但還沒有被分配到CPU即不一定會被立即執(zhí)行,此時處于線程就緒隊列,等待系統(tǒng)為其分配CPCU,等待狀態(tài)并不是執(zhí)行狀態(tài); 此時線程是活著的(alive)。
[3] 運行狀態(tài)(Running):一旦獲取CPU(被JVM選中),線程就進(jìn)入運行(running)狀態(tài),線程的run()方法才開始被執(zhí)行;在運行狀態(tài)的線程執(zhí)行自己的run()方法中的操作,直到調(diào)用其他的方法而終止、或者等待某種資源而阻塞、或者完成任務(wù)而死亡;如果在給定的時間片內(nèi)沒有執(zhí)行結(jié)束,就會被系統(tǒng)給換下來回到線程的等待狀態(tài);此時線程是活著的(alive)。
[4] 阻塞狀態(tài)(Blocked):通過調(diào)用join()、sleep()、wait()或者資源被暫用使線程處于阻塞(blocked)狀態(tài);處于Blocking狀態(tài)的線程仍然是活著的(alive)。
[5] 死亡狀態(tài)(Dead):當(dāng)一個線程的run()方法運行完畢或被中斷或被異常退出,該線程到達(dá)死亡(dead)狀態(tài)。此時可能仍然存在一個該Thread的實例對象,當(dāng)該Thread已經(jīng)不可能在被作為一個可被獨立執(zhí)行的線程對待了,線程的獨立的call stack已經(jīng)被dissolved。一旦某一線程進(jìn)入Dead狀態(tài),他就再也不能進(jìn)入一個獨立線程的生命周期了。對于一個處于Dead狀態(tài)的線程調(diào)用start()方法,會出現(xiàn)一個運行期(runtime exception)的異常;處于Dead狀態(tài)的線程不是活著的(not alive)。 線程的狀態(tài)轉(zhuǎn)換圖如下所示:
?
?
5.線程的方法、屬性
[1] 優(yōu)先級(priority) 每個類都有自己的優(yōu)先級,一般property用1-10的整數(shù)表示,默認(rèn)優(yōu)先級是5,優(yōu)先級最高是10;優(yōu)先級高的線程并不一定比優(yōu)先級低的線程執(zhí)行的機會高,只是執(zhí)行的機率高;默認(rèn)一個線程的優(yōu)先級和創(chuàng)建他的線程優(yōu)先級相同。[2] Thread.sleep()/sleep(long millis)
當(dāng)前線程睡眠/millis的時間(millis指定睡眠時間是其最小的不執(zhí)行時間,因為sleep(millis)休眠到達(dá)后,無法保證會被JVM立即調(diào)度);sleep()是一個靜態(tài)方法(static method) ,所以他不會停止其他的線程也處于休眠狀態(tài);線程sleep()時不會失去擁有的對象鎖。 作用:保持對象鎖,讓出CPU,調(diào)用目的是不讓當(dāng)前線程獨自霸占該進(jìn)程所獲取的CPU資源,以留一定的時間給其他線程執(zhí)行的機會。
[3] Thread.yield()
讓出CPU的使用權(quán),給其他線程執(zhí)行機會、讓同等優(yōu)先權(quán)的線程運行(但并不保證當(dāng)前線程會被JVM再次調(diào)度、使該線程重新進(jìn)入Running狀態(tài)),如果沒有同等優(yōu)先權(quán)的線程,那么yield()方法將不會起作用。
[4] Thread.join()
使用該方法的線程會在此之間執(zhí)行完畢后再往下繼續(xù)執(zhí)行。
[5] object.wait()
當(dāng)一個線程執(zhí)行到wait()方法時,他就進(jìn)入到一個和該對象相關(guān)的等待池(Waiting Pool)中,同時失去了對象的機鎖---暫時的,wait后還要返還對象鎖。當(dāng)前線程必須擁有當(dāng)前對象的鎖,如果當(dāng)前線程不是此鎖的擁有者,會拋出IllegalMonitorStateException異常,所以wait()必須在synchronized block中調(diào)用。
[6] Object.notify()/notifyAll()
喚醒在當(dāng)前對象等待池中等待的第一個線程/所有線程。notify()/notifyAll()也必須擁有相同對象鎖,否則也會拋出IllegalMonitorStateException異常。
[7] Synchronizing Block
Synchronized Block/方法控制對類成員變量的訪問;Java中的每一個對象都有唯一的一個內(nèi)置的鎖,每個Synchronized Block/方法只有持有調(diào)用該方法被鎖定對象的鎖才可以訪問,否則所屬線程阻塞;機鎖具有獨占性、一旦被一個Thread持有,其他的Thread就不能再擁有(不能訪問其他同步方法),方法一旦執(zhí)行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。
6.Object.wait()和Thread.sleep(long)的區(qū)別
[1] sleep()方法sleep()使當(dāng)前線程進(jìn)入停滯狀態(tài)(阻塞當(dāng)前線程),讓出CUP的使用、目的是不讓當(dāng)前線程獨自霸占該進(jìn)程所獲的CPU資源,以留一定時間給其他線程執(zhí)行的機會。
sleep()是Thread類的Static(靜態(tài))的方法;因此他不能改變對象的機鎖,所以當(dāng)在一個Synchronized塊中調(diào)用Sleep()方法是,線程雖然休眠了,但是對象的機鎖并木有被釋放,其他線程無法訪問這個對象(即使睡著也持有對象鎖)。
在sleep()休眠時間期滿后,該線程不一定會立即執(zhí)行,這是因為其它線程可能正在運行而且沒有被調(diào)度為放棄執(zhí)行,除非此線程具有更高的優(yōu)先級。
[2] wait()方法
wait()方法是Object類里的方法;當(dāng)一個線程執(zhí)行到wait()方法時,它就進(jìn)入到一個和該對象相關(guān)的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)超時時間到后還需要返還對象鎖),其他線程可以訪問。
wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當(dāng)前等待池中的線程。
wiat()必須放在synchronized block中,否則會在program runtime時扔出"java.lang.IllegalMonitorStateException"異常。 總結(jié)一下:
wait():可以指定時間也可以不指定時間,不指定時間時,只能由對應(yīng)的notify()或notifyAll()來喚醒。
sleep():必須指定時間,時間到自動從凍結(jié)狀態(tài)轉(zhuǎn)入運行狀態(tài)或臨時阻塞狀態(tài)。
wait():線程會釋放執(zhí)行權(quán),并釋放鎖。
sleep():線程會釋放執(zhí)行權(quán),但是并不釋放鎖。
轉(zhuǎn)載于:https://www.cnblogs.com/lc-java/p/7519620.html
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Thread 小总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: InnoDB Master Thread
- 下一篇: maven仓库找不到需要的jar包解决办