摘要:死亡狀態(tài)有兩個(gè)原因會(huì)導(dǎo)致線程死亡方法正常退出而自然死亡。一個(gè)未捕獲的異常終止了方法而使線程猝死。注意,放入的線程不必?fù)?dān)心其結(jié)束,超過不活動(dòng),其會(huì)自動(dòng)被終止。線程間相互干擾描述了當(dāng)多個(gè)線程訪問共享數(shù)據(jù)時(shí)可能出現(xiàn)的錯(cuò)誤。
線程 進(jìn)程與線程的區(qū)別
線程是指進(jìn)程內(nèi)的一個(gè)執(zhí)行單元,也是進(jìn)程內(nèi)的可調(diào)度實(shí)體。
一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程。
新建狀態(tài)(New):例如new Thread(r)。
就緒狀態(tài)(Runnable): 當(dāng)start()方法返回后,線程就處于就緒狀態(tài)。
運(yùn)行狀態(tài)(Running) :當(dāng)線程獲得CPU時(shí)間后,它才進(jìn)入運(yùn)行狀態(tài),真正開始執(zhí)行run()方法。
阻塞狀態(tài):(Blocked)
線程運(yùn)行過程中,可能由于各種原因進(jìn)入阻塞狀態(tài):
線程通過調(diào)用sleep方法進(jìn)入睡眠狀態(tài)。
線程調(diào)用一個(gè)在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會(huì)返回到它的調(diào)用者。
線程試圖得到一個(gè)鎖,而該鎖正被其他線程持有。
線程在等待某個(gè)觸發(fā)條件。
死亡狀態(tài)(Dead)
有兩個(gè)原因會(huì)導(dǎo)致線程死亡:
run方法正常退出而自然死亡。
一個(gè)未捕獲的異常終止了run方法而使線程猝死。
為了確定線程在當(dāng)前是否存活著(就是要么是可運(yùn)行的,要么是被阻塞了),需要使用isAlive方法。如果是可運(yùn)行或被阻塞,這個(gè)方法返回true; 如果線程仍舊是new狀態(tài)且不是可運(yùn)行的, 或者線程死亡了,則返回false。
如何創(chuàng)建一個(gè)線程從Java.lang.Thread類派生一個(gè)新的線程類,重寫它的run()方法。
實(shí)現(xiàn)Runnalbe接口,重寫Runnalbe接口中的run()方法。
實(shí)現(xiàn)Callable 接口,重寫Callable接口中的call()方法。
Runnable和Callable的區(qū)別Callable規(guī)定的方法是call(),Runnable規(guī)定的方法是run()。
Callable的任務(wù)執(zhí)行后可返回值,而Runnable的任務(wù)是不能返回值的。
call方法可以拋出異常,run方法不可以。
運(yùn)行Callable任務(wù)可以拿到一個(gè)Future對(duì)象,F(xiàn)uture 表示異步計(jì)算的結(jié)果。它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并獲取計(jì)算的結(jié)果。
如何安全退出一個(gè)已啟動(dòng)的線程使用退出標(biāo)志:
當(dāng)run方法執(zhí)行完后,線程就會(huì)退出。但有時(shí)run方法是永遠(yuǎn)不會(huì)結(jié)束的,如在服務(wù)端程序中使用線程進(jìn)行監(jiān)聽客戶端請(qǐng)求,或是其他的需要循環(huán)處理的任務(wù)。在這種情況下,一般是將這些任務(wù)放在一個(gè)循環(huán)中,如while循環(huán)。如果想使while循環(huán)在某一特定條件下退出,最直接的方法就是設(shè)一個(gè)boolean類型的標(biāo)志,并通過設(shè)置這個(gè)標(biāo)志為true或false來控制while循環(huán)是否退出。
對(duì)于sleep()方法,我們首先要知道該方法是屬于Thread類中的。而wait()方法,則是屬于Object類中的。
sleep()方法導(dǎo)致了程序暫停執(zhí)行指定的時(shí)間,讓出cpu該其他線程,但是他的監(jiān)控狀態(tài)依然保持者,當(dāng)指定的時(shí)間到了又會(huì)自動(dòng)恢復(fù)運(yùn)行狀態(tài)。
在調(diào)用sleep()方法的過程中,線程不會(huì)釋放對(duì)象鎖。而當(dāng)調(diào)用wait()方法的時(shí)候,線程會(huì)放棄對(duì)象鎖,進(jìn)入等待此對(duì)象的等待鎖定池,只有針對(duì)此對(duì)象調(diào)用notify()方法后本線程才進(jìn)入對(duì)象鎖定池準(zhǔn)備。
線程池由于線程的生命周期中包括創(chuàng)建、就緒、運(yùn)行、阻塞、銷毀階段,當(dāng)我們待處理的任務(wù)數(shù)目較小時(shí),我們可以自己創(chuàng)建幾個(gè)線程來處理相應(yīng)的任務(wù),但當(dāng)有大量的任務(wù)時(shí),由于創(chuàng)建、銷毀線程需要很大的開銷,運(yùn)用線程池這些問題就大大的緩解了。
相關(guān)參數(shù):
corePoolSize 核心池的大小
maximumPoolSize 線程池最大線程數(shù)
keepAliveTime 表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止
unit 參數(shù)keepAliveTime的時(shí)間單位
workQueue 一個(gè)阻塞隊(duì)列,用來存儲(chǔ)等待執(zhí)行的任務(wù)
threadFactory 線程工廠,主要用來創(chuàng)建線程
handler 表示當(dāng)拒絕處理任務(wù)時(shí)的策略
ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(corePoolSize,// 核心池的大小 maximumPoolSize, //線程池最大線程數(shù) keepAliveTime, // 閑置線程存活時(shí)間 TimeUnit.MILLISECONDS,// 時(shí)間單位 new LinkedBlockingDeque(),//一個(gè)阻塞隊(duì)列,用來存儲(chǔ)等待執(zhí)行的任務(wù) Executors.defaultThreadFactory(),//線程工廠,主要用來創(chuàng)建線程 new AbortPolicy()// 隊(duì)列已滿,而且當(dāng)前線程數(shù)已經(jīng)超過最大線程數(shù)時(shí)的異常處理策略 (表示當(dāng)拒絕處理任務(wù)時(shí)的策略) );
當(dāng)提交的任務(wù)數(shù)達(dá)到coolPoolSize大小后,之后提交的任務(wù)會(huì)被保存到workQueue中,而不是創(chuàng)建新的線程去執(zhí)行它們。當(dāng)workQueue充滿后,就會(huì)去創(chuàng)建新的線程,但是總的線程數(shù)量不會(huì)大于maximumPoolSize。
當(dāng)前線程數(shù)量大于corePoolSize的時(shí)候,如果空閑線程等待的時(shí)間超過了keepAliveTime那么這個(gè)空閑線程就會(huì)被銷毀,當(dāng)然如果當(dāng)前線程數(shù)量沒有超過corePoolSize,那么這個(gè)keepAliveTime是不起作用的。
常用線程池
newCachedThreadPool
緩存型池子,先查看池中有沒有以前建立的線程,如果有,就reuse,如果沒有,就建立一個(gè)新的線程加入池中。
緩存型池子,通常用于執(zhí)行一些生存周期很短的異步型任務(wù);因此一些面向連接的daemon型server中用得不多。
能reuse的線程,必須是timeout IDLE內(nèi)的池中線程,缺省timeout是60s,超過這個(gè)IDLE時(shí)長(zhǎng),線程實(shí)例將被終止及移出池。
注意,放入CachedThreadPool的線程不必?fù)?dān)心其結(jié)束,超過TIMEOUT不活動(dòng),其會(huì)自動(dòng)被終止。
newFixedThreadPool
newFixedThreadPool與cacheThreadPool差不多,也是能reuse就用,但不能隨時(shí)建新的線程。
其獨(dú)特之處:任意時(shí)間點(diǎn),最多只能有固定數(shù)目的活動(dòng)線程存在,此時(shí)如果有新的線程要建立,只能放在另外的隊(duì)列中等待,直到當(dāng)前的線程中某個(gè)線程終止直接被移出池子。
和cacheThreadPool不同,F(xiàn)ixedThreadPool沒有IDLE機(jī)制(可能也有,但既然文檔沒提,肯定非常長(zhǎng),類似依賴上層的TCP或UDP IDLE機(jī)制之類的),所以FixedThreadPool多數(shù)針對(duì)一些很穩(wěn)定很固定的正規(guī)并發(fā)線程,多用于服務(wù)器。
從方法的源代碼看,cache池和fixed 池調(diào)用的是同一個(gè)底層池,只不過參數(shù)不同:fixed池線程數(shù)固定,并且是0秒IDLE(無IDLE),cache池線程數(shù)支持0-Integer.MAX_VALUE(顯然完全沒考慮主機(jī)的資源承受能力),60秒IDLE 。
ScheduledThreadPool
調(diào)度型線程池。
這個(gè)池子里的線程可以按schedule依次delay執(zhí)行,或周期執(zhí)行。
SingleThreadExecutor
單例線程,任意時(shí)間池中只能有一個(gè)線程。
用的是和cache池和fixed池相同的底層池,但線程數(shù)目是1-1,0秒IDLE(無IDLE)。
多線程 多線程問題生成的原因線程之間的共享變量存儲(chǔ)在主內(nèi)存(main memory)中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存(local memory),本地內(nèi)存中存儲(chǔ)了該線程以讀/寫共享變量的副本。
對(duì)于一個(gè)簡(jiǎn)單的 i++ 操作,會(huì)發(fā)生如下的步驟:
read:作用于主內(nèi)存中,把主內(nèi)存中一個(gè)變量的值傳輸?shù)?工作內(nèi)存 中。
load:作用于工作內(nèi)存,把從read 操作從主內(nèi)存中得到的值放入到工作內(nèi)存的副本中。
use:把工作內(nèi)存中的該副本值傳遞給執(zhí)行引擎(也就是操作數(shù)棧中)。
assign:作用于工作內(nèi)存,把執(zhí)行引擎執(zhí)行后的新值傳遞給該工作內(nèi)存的變量。
store:作用于工作內(nèi)存,把工作內(nèi)存中該變量的值傳送到 主內(nèi)存中去。
write:作用于主內(nèi)存的變量,把store 操作 得到的值寫入到 主內(nèi)存的該變量中。
所以說,一個(gè) i++ 操作并不是原子性的。這上述的這些步驟中,可能會(huì)有其他線程對(duì)主內(nèi)存的變量進(jìn)行操作,從而導(dǎo)致出現(xiàn)多線程問題。
什么是線程安全線程安全就是說多線程訪問同一代碼,不會(huì)產(chǎn)生不確定的結(jié)果。編寫線程安全的代碼是低依靠線程同步。
線程同步線程間的通訊首要的方式就是對(duì)字段及其字段所引用的對(duì)象的共享訪問。這種通信方式是及其高效的,但是也是導(dǎo)致了可能的錯(cuò)誤:線程間相互干涉和內(nèi)存一致性的問題。避免出現(xiàn)這兩種錯(cuò)誤的方法就是同步。
線程間相互干擾描述了當(dāng)多個(gè)線程訪問共享數(shù)據(jù)時(shí)可能出現(xiàn)的錯(cuò)誤。
內(nèi)存一致性錯(cuò)誤描述的了共享內(nèi)存可能導(dǎo)致的錯(cuò)誤。
同步方法(Synchronized method)描述了一種簡(jiǎn)單的可以有效防止線程間相互干擾及其內(nèi)存一致性錯(cuò)誤的方法。
明鎖及同步描述了一種更加通用的同步方法,以及同步是如何基于明鎖而實(shí)現(xiàn)的。
原子性描述了不能被其它線程干擾的操作。
1. Lock是一個(gè)接口,而synchronized是Java中的關(guān)鍵字,synchronized是內(nèi)置的語言實(shí)現(xiàn)。
2. synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放線程占有的鎖,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生;而Lock在發(fā)生異常時(shí),如果沒有主動(dòng)通過unLock()去釋放鎖,則很可能造成死鎖現(xiàn)象,因此使用Lock時(shí)需要在finally塊中釋放鎖。
3. Lock可以讓等待鎖的線程響應(yīng)中斷,而synchronized卻不行,使用synchronized時(shí),等待的線程會(huì)一直等待下去,不能夠響應(yīng)中斷。
4. 通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。
5. Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/70076.html
摘要:看到的只是,而由泛型附加的類型信息對(duì)來說是不可見的。然后再加載執(zhí)行類的靜態(tài)變量以及靜態(tài)語句塊。接口中基本數(shù)據(jù)類型為而抽類象不是的。本地方法接口主要是調(diào)用或?qū)崿F(xiàn)的本地方法及返回結(jié)果。用戶自定義類加載器,在程序運(yùn)行期間,通過的子類動(dòng)態(tài)加載。 編譯機(jī)制 編譯主要是把?.Java文件轉(zhuǎn)換為 .class 文件。其中轉(zhuǎn)換后的 .class 文件就包含了元數(shù)據(jù),方法信息等一些信息。比如說元數(shù)據(jù)就...
摘要:接下來就是會(huì)把任務(wù)提交到隊(duì)列中給線程池調(diào)度處理因?yàn)橹饕P(guān)心的是這個(gè)線程怎么執(zhí)行,異常的拋出和處理,所以我們暫時(shí)不解析多余的邏輯。 前言 今天遇到了一個(gè)bug,現(xiàn)象是,一個(gè)任務(wù)放入線程池中,似乎沒有被執(zhí)行,日志也沒有打。 經(jīng)過本地代碼調(diào)試之后,發(fā)現(xiàn)在任務(wù)邏輯的前半段,拋出了NPE,但是代碼外層沒有try-catch,導(dǎo)致這個(gè)異常被吃掉。 這個(gè)問題解決起來是很簡(jiǎn)單的,外層加個(gè)try-cat...
閱讀 2967·2021-11-08 13:20
閱讀 1034·2021-09-22 15:20
閱讀 665·2019-08-30 15:53
閱讀 1972·2019-08-30 15:43
閱讀 1284·2019-08-29 17:21
閱讀 543·2019-08-29 12:15
閱讀 2381·2019-08-28 17:51
閱讀 3147·2019-08-26 13:26