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