線程是由內(nèi)核自動(dòng)調(diào)度的運(yùn)行在進(jìn)程上下文中的邏輯流。
基本概念
線程有自己的上下文:唯一的Thread ID、棧、棧指針、程序計(jì)數(shù)器、通用目的寄存器、條件碼。
并發(fā)與并行
并發(fā):在單核多任務(wù)或者任務(wù)數(shù)大于核數(shù)的情況下,對(duì)于某個(gè)cpu來說,會(huì)把cpu執(zhí)行時(shí)間分為幾個(gè)時(shí)間片,交替運(yùn)行任務(wù)。在一個(gè)時(shí)間片內(nèi)執(zhí)行某個(gè)任務(wù)后(可能沒執(zhí)行完),會(huì)掛起當(dāng)前的任務(wù)然后執(zhí)行其他的任務(wù)。掛起前會(huì)保留線程的上下文以便從中斷點(diǎn)恢復(fù)cpu狀態(tài),例如:cpu寄存器(記錄掛起前變量值等等),程序計(jì)數(shù)器(記錄線程執(zhí)行到哪條指令了)等等。
并行:多核情況下,兩個(gè)任務(wù)分別在不同cpu執(zhí)行,兩個(gè)任務(wù)互不搶占CPU資源,可以同時(shí)進(jìn)行,這種方式我們稱之為并行(Parallel)。
分類
-
守護(hù)線程
在操作系統(tǒng)中,是沒有守護(hù)線程概念的,只有守護(hù)進(jìn)程。jvm借鑒了unix守護(hù)進(jìn)程的概念,構(gòu)建了對(duì)自己有利的守護(hù)線程。有以下特點(diǎn):- 為用戶線程服務(wù)的
- 優(yōu)先級(jí)較低,只剩下守護(hù)線程的話,jvm就會(huì)退出
- 典型的例子GC Daemon,當(dāng)就剩下GC Daemon時(shí),就沒有垃圾產(chǎn)生,就沒有存在的必要了,jvm就會(huì)退出
- 在Daemon線程中產(chǎn)生的新線程也是Daemon的,而守護(hù)進(jìn)程fork()出來的子進(jìn)程不再是守護(hù)進(jìn)程。
用戶線程:非守護(hù)線程
線程的狀態(tài)
分類
新建(New):準(zhǔn)備線程執(zhí)行需要的一些條件,例如線程自己的一些上下文
就緒(Runnable):線程準(zhǔn)備好的條件滿足后進(jìn)入就緒狀態(tài)或者Running的時(shí)間片到了也會(huì)進(jìn)入就緒狀態(tài)
運(yùn)行 (Running):就緒狀態(tài)的線程分配到了cpu時(shí)間片
定時(shí)等待(Timed Waiting):時(shí)間內(nèi)的等待
等待(Waiting):主動(dòng)睡眠,等待被喚醒
阻塞(Blocked):IO阻塞或者等待鎖
終止(Terminated):死亡
線程運(yùn)行中會(huì)被某些原因所打斷進(jìn)入阻塞(詳細(xì)分為Timed Waiting,Waiting,Blocked)或者Runnable(時(shí)間片到了)
線程狀態(tài)的轉(zhuǎn)換
- start()方法標(biāo)識(shí)啟動(dòng)一個(gè)線程,并分配資源。
- yield()交出時(shí)間分片讓高優(yōu)先級(jí)的線程先執(zhí)行,如果持有鎖的話是不會(huì)釋放掉的。
- sleep()方法會(huì)交出時(shí)間分片,如果持有鎖的話是不會(huì)釋放掉的。
- Object.wait()交出cpu時(shí)間,并且釋放掉鎖。
- 使用wait(),notify(),notifyAll()前先拿到鎖后才能執(zhí)行,因?yàn)榉椒ɑ蛘叽a塊執(zhí)行完才能釋放鎖,所以在方法里使用notify()或者notifyAll()后最好不要跟太耗cpu時(shí)間的計(jì)算。
- new Object().wait() 編譯不會(huì)報(bào)錯(cuò),但運(yùn)行會(huì)報(bào)java.lang.IllegalMonitorStateException
- join()調(diào)用了Object.wait()方法。
- interrupt()可以中斷正在處于阻塞(Waiting,Timed Waiting,不包括Blocked)的線程,使阻塞線程拋出InterruptedException,sleep、wait、join這些方法都會(huì)拋出InterruptedException。 interrupt()會(huì)把中斷標(biāo)志位置為true。
- interrupt()不可中斷不處于阻塞的線程,但是interrupt()后如果用isInterrupted()來判斷標(biāo)志位(是否中斷)也可以達(dá)到目的。
- 有些操作是不適合用Daemon線程的,例如讀寫操作,因?yàn)樵贒aemon Thread還沒來的及進(jìn)行操作時(shí),虛擬機(jī)可能已經(jīng)退出了。
- thread.setDaemon(true)必須在start()之前設(shè)置,否則會(huì)拋IllegalThreadStateException異常。
因?yàn)門hread類用了代理模式,實(shí)現(xiàn)了Runnable接口的run方法。
可以使用new Thread(被代理的Runnable實(shí)例).start(); 執(zhí)行的是被代理類的run;
當(dāng)然也可以直接繼承Thread覆寫run方法。
下篇詳細(xì)剖析下JCU里的老大-AQS,請(qǐng)大家保持關(guān)注哦。