線程的4個狀態:
創建(new),就緒(runnable),阻塞(blocked),終止(dead)
Runnable接口:
定義了一個描述任務的方式,他具有任何內在的線程能力,使用它必須將它顯示的依附在線程上。
傳統的方法是將其傳遞給一個Thread的構造方法。
Thread對象:
當主線程創建Thread對象后,并沒有創建其對象的引用,在start()之后每個Thread都會注冊自己,并創建自己一個單獨的執行線程,在其執行完run()方法并死亡前是無法回收該對象的。
Exector:
線程池可以為我們管理Thread對象,相當于在客戶端與執行任務之間的一個間接層。
ExcetorService由Excetor的靜態方法創建。
newCachedThreadpool()會在程序中創建與任務數相同的線程, 然后在回收舊線程時才會停止創建新線程。當所需要的線程的數量過多影響到性能時應選用newFixedThreadpool();
newFixedThreadpool()可以指定線程數量的上限。
newSingleThreadExector()數量為1的FixedThreadpool,所有任務以隊列的形式一個個順序運行。
shutdown()方法防止其他任務提交給該Excetor.
Callable接口與Future:
是帶有返回值的Runnable接口,內部聲明的方法是call().一般情況下配合ExctoeService使用。Future就是對于具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果。必要時可以通過get方法獲取執行結果,該方法會阻塞直到任務返回結果。Future由ExecutorService.sumbit返回。
Future提供了三種功能:
1)判斷任務是否完成;
2)能夠中斷任務;
3)能夠獲取任務執行結果。
因為Future只是一個接口,所以是無法直接用來創建對象使用的,因此有FutureTask。
FutureTask:
publicclassFutureTaskimplementsRunnableFuture
FutureTask類實現了RunnableFuture接口,我們看一下RunnableFuture接口的實現:
publicinterfaceRunnableFutureextendsRunnable, Future {
voidrun();
}
可以看出RunnableFuture繼承了Runnable接口和Future接口,而FutureTask實現了RunnableFuture接口。所以它既可以作為Runnable被線程執行,又可以作為Future得到Callable的返回值。
FutureTask提供了2個構造器:
publicFutureTask(Callable callable) {
}
publicFutureTask(Runnable runnable, V result) {
}
事實上,FutureTask是Future接口的一個唯一實現類。
后臺線程:
調用setDaemon方法可以將線程設置成后臺線程,但是必須在線程start后才能起作用。當程序終止時就會殺死全部后臺線程。后臺線程派生出的子線程全部默認為后臺線程。
線程中捕獲異常:
線程中產生的異常不能夠在主線程捕獲。如果需要在主線程捕獲子線程產生的異常需要用到uncanughtException.
uncanughtException是一個接口。實現該接口,并調用Thread.setUncanughtExceptionHandle(..),就可以在指定的線程中捕獲其他線程產生的異常。
synchronized與Lock的區別:
ReentrantLock是Lock的一個實現,他允許嘗試獲得鎖但最終未獲得鎖,因為可以決定離開去做別事情。而synchronized則會等待鎖。因此ReentrantLock賦予了更細粒度的控制力。
原子性:
一個操作是原子性去取決于其底層的實現,因此是與操作系統有關,應避免使用原子性來替代線程同步。
線程的終止:
當線程被阻塞時(如調用了wait/sleep等方法時),可以使用 Thread.interrupt()方法打斷阻塞的狀態,當調用此方法后 Thread的 interrupted 標記被設置為 true. 此時如果你調用 interrupted()方法來測試中斷狀態(此方法會清空中斷狀態,也可以使用isInterrupted()來測試中斷狀態,這時中斷狀態不會被清空).
當調用 interrupte()方法時會拋出 InterruptedException,這時中斷狀態被清空.
線程被 sleep/wait 阻塞時可以被 interrupt,但如果線程被 IO或synchronized 阻塞則不可以被 interrupt,
線程間的協作:
我們上面講的主要是如何讓線程互斥的訪問一個共享的資源,現在要研究如何讓線程間進行協作,也就說二個線程有個先后,必須第一個線程完成后,第二個線程才能開始執行(像生產者與消費者)。
要完成上面所說的就要用到 Object.wait()/notify()/notifyAll()了.
wait/notify/notifyAll 的調用都必須在獲得這個對象的 monitor 時.
當wait 被調用時,當前線程被加入 wait set,這時這個線程不再被系統調度,直到有其它線程調用特定對象的notify或notifyAll方法時,此線程從wait set 中移除,開始被系統調度,但它必須再次獲得特定對象的monitor才能繼續。當獲得對象的monitor后,wait方法會返回,這時此線程會恢復到調用wait方法前的狀態.
注意: 當調用 wait()時會釋放對象鎖,而 sleep/yield 等方法不會釋放鎖.
wait/notify/notifyAll 及 synchronized 都是相對于一個對象來說的,都需要這個對象的鎖.