多線程:多線程允許我們可以"同時(shí)"執(zhí)行多段代碼。
實(shí)際上多線程是并發(fā)運(yùn)行的荔茬,即:JVM中的線程調(diào)度會為多個(gè)線程分配"CPU時(shí)間片"霸琴,并將這些時(shí)間片盡可能均勻的分配給線程雄家,當(dāng)一個(gè)線程獲取時(shí)間片后氮双,該線程的任務(wù)代碼被CPU執(zhí)行碰酝,其他線程處于等待狀態(tài)。這種宏觀上同時(shí)運(yùn)行而微觀上走走停停的現(xiàn)象稱為并發(fā)戴差。
java中的線程是由Thread的實(shí)例表示送爸。而Thread的創(chuàng)建有兩種方式:
1)繼承Thread并重寫run方法
第一種創(chuàng)建線程的方式雖然定義簡單,但也存在一些不足:
由于java是單繼承的暖释,這經(jīng)常導(dǎo)致在實(shí)際開發(fā)中碱璃,為了復(fù)用一個(gè)類的方法,我們需要繼承那個(gè)類饭入,而自身又希望是一個(gè)線程時(shí)導(dǎo)致的繼承沖突。
繼承了線程需要重寫run方法來定義該線程執(zhí)行的任務(wù)代碼肛真,這就導(dǎo)致了線程與執(zhí)行的任務(wù)有一個(gè)必然的耦合關(guān)系谐丢,不利于線程的重用。
2)實(shí)現(xiàn)Runnable接口并重寫run方法來單獨(dú)定義任務(wù)
下面示例怎么用匿名內(nèi)部類完成線程的兩種方式創(chuàng)建:
線程提供了一個(gè)靜態(tài)方法:static Thread currentThread()
該方法可以獲取運(yùn)行這個(gè)方法的線程
java中所有代碼都是靠線程運(yùn)行的蚓让,main方法也不例外乾忱。只不過運(yùn)行main方法的線程不是由我們創(chuàng)建的。
獲取線程信息的相關(guān)方法:
線程優(yōu)先級:
對于線程調(diào)度的工作趟卸,線程不能干涉蹄葱,即:線程只能被動的等待分配CPU時(shí)間片,而不能主動獲取锄列⊥荚疲可以通過修改線程優(yōu)先級來最大程度改善獲取CPU時(shí)間片的幾率,理論上邻邮,線程優(yōu)先級越高的線程獲取CPU時(shí)間片的次數(shù)越多竣况。
線程的優(yōu)先級有10個(gè)等級,分別用整數(shù)1-10表示筒严。其中1最低丹泉,10最高情萤,5為默認(rèn)值。
線程提供了一個(gè)靜態(tài)方法:static void sleep(long ms)
該方法會將運(yùn)行當(dāng)前方法的線程阻塞指定毫秒。
守護(hù)線程----也稱為后臺線程
默認(rèn)創(chuàng)建出來的線程都是前臺線程睬塌,若要設(shè)置為后臺線程
可以通過線程提供的方法setDaemon來完成泉蝌。
后臺線程使用上與前臺線程一樣,但是在結(jié)束時(shí)機(jī)上有一點(diǎn)是不同的揩晴,即:當(dāng)一個(gè)進(jìn)程結(jié)束時(shí)勋陪,所有正在運(yùn)行的后臺線程都會強(qiáng)制結(jié)束。而進(jìn)程的結(jié)束是當(dāng)一個(gè)進(jìn)程中所有前臺線程都結(jié)束時(shí)結(jié)束硫兰。
所以將來開發(fā)中可以將一直保持運(yùn)行的任務(wù)诅愚,但是可以隨著程序一同結(jié)束的放在后臺線程上運(yùn)行。
下面示例可以想象為泰坦尼克號中rose(一個(gè)前臺進(jìn)程)跳了之后劫映,jack(守護(hù)進(jìn)程)也隨之jump
線程提供了一個(gè)方法:join
join可以協(xié)調(diào)線程間同步運(yùn)行
網(wǎng)頁上文字與圖片顯示案例----文字先顯示违孝,等待圖片下載完畢然后顯示
final Thread download。泳赋。雌桑。原因:
當(dāng)一個(gè)方法的局部內(nèi)部類中需要引用該方法的其他局部變量時(shí),該變量必須是final的
在這里main方法的局部內(nèi)部類show中想引用main方法的其他局部變量download,那么download就必須是final修飾的祖今。JDK1.8之后由于內(nèi)存問題被重新定義校坑,不在有這個(gè)問題,所以就不再需要做上述設(shè)定千诬。
多線程并發(fā)安全問題:
當(dāng)多個(gè)線程并發(fā)訪問統(tǒng)一資源時(shí)耍目,由于線程切換時(shí)機(jī)不確定,導(dǎo)致代碼未按照設(shè)計(jì)方式的順序執(zhí)行導(dǎo)致的邏輯混亂徐绑。嚴(yán)重時(shí)可能導(dǎo)致系統(tǒng)癱瘓邪驮。
解決多線程并發(fā)安全的手段是將"各干各的"變?yōu)?排隊(duì)執(zhí)行":
當(dāng)一個(gè)方法被synchronized修飾后,那么該方法稱為“同步方法”傲茄,即:多個(gè)線程不能同時(shí)進(jìn)入到方法
內(nèi)部執(zhí)行毅访。
在方法上使用synchronized修飾后,上鎖的對象就是當(dāng)前方法所屬對象盘榨,即:方法中看到的this
為避免過多的拖慢系統(tǒng)處理速度俺抽,有效的縮小同步范圍可以在保證并發(fā)安全的前提下提高并發(fā)的效率。
靜態(tài)方法使用synchronized较曼,那么一定具有同步效果
靜態(tài)方法上鎖的對象是該方法所屬類的類對象磷斧。實(shí)際上JVM在加載一個(gè)類的class文件時(shí),會實(shí)例化一個(gè)Class類型的實(shí)例去保存該類的信息(屬性,方法等)弛饭。所以JVM中每個(gè)加載過的類都有且只有一個(gè)Class的實(shí)例用于表示它這個(gè)Class的實(shí)例就是該類的類對象冕末。
互斥鎖:
將集合或Map轉(zhuǎn)換為線程安全的:
但是要注意:線程安全的集合也不與迭代器遍歷該集合的操作互斥侣颂。但是迭代器要求遍歷的過程中不能通過集合的方法增刪元素档桃,否則會拋出異常,所以在多個(gè)線程間有這樣的操作時(shí)憔晒,需要自行維護(hù)遍歷集合與集合元素操作間的互斥關(guān)系藻肄。
線程池----主要解決兩個(gè)問題:
1)控制線程數(shù)量。因?yàn)榫€程數(shù)多了拒担,會導(dǎo)致內(nèi)存開銷大嘹屯,嚴(yán)重時(shí)會導(dǎo)致系統(tǒng)癱瘓,并且由于線程數(shù)量多會導(dǎo)致CPU過度切換从撼,拖慢系統(tǒng)響應(yīng)州弟。
2)重用線程
BlockingQueue是雙緩沖隊(duì)列。
在多線程并發(fā)時(shí)低零,若需要使用隊(duì)列婆翔,我們可以使用Queue,但是要解決一個(gè)問題就是同步掏婶,但同步操作會降低并發(fā)對Queue操作的效率啃奴。
BlockingQueue內(nèi)部使用兩條隊(duì)列,可允許兩個(gè)線程同時(shí)向隊(duì)列一個(gè)做存儲雄妥,一個(gè)做取出操作纺腊。在保證并發(fā)安全的同時(shí)提高了隊(duì)列的存取效率。
創(chuàng)建方式如下:
BlockingQueue queue = new LinkedBlockingQueue();