最近項目組組織學習線程池,也受到了打擊较鼓,以后堅持做筆記不偷懶了。
要了解線程池先了解幾個相關(guān)的概念
線程:進程中負責執(zhí)行的單元
進程:進程有獨立的代碼和數(shù)據(jù)空間(一個app)违柏,一個進程可以有多個線程
多線程:解決多任務(wù)執(zhí)行的需求笨腥,合理使用cpu的資源,線程的切換由cpu決定勇垛,因此那個線程在執(zhí)行具有不確定性
線程池:思想就是開辟出一個對象池的思想脖母,就是開辟出一塊內(nèi)存空間,存放很多未死的線程闲孤,池中線程的調(diào)度由池管理器來管理谆级,當有線程任務(wù)時從池中取出線程執(zhí)行烤礁,執(zhí)行完成后線程對象放回池中,這樣可以避免反復創(chuàng)建線程對象帶來的性能開銷肥照,節(jié)約了系統(tǒng)資源脚仔。
創(chuàng)建線程的三種方式
1、繼承Thread類舆绎,覆蓋run()方法鲤脏。創(chuàng)建線程對象并用start()方法啟動線程。
2吕朵、實現(xiàn)Runnable接口來創(chuàng)建Thread線程 run();
3猎醇、通過實現(xiàn)Callable接口來創(chuàng)建Thread線程 call()
? ? ? Thread 類中的 start() 和 run() 方法,start會重新啟動線程
? ? ?系統(tǒng)接口HandlerThread 繼承了Thread,他是可以使用Handler的Thread,一個具有消息循環(huán)的線程努溃。 ? ? ? ? ? ?run()方法中通過Looper.prepare()來創(chuàng)建消息隊列硫嘶,通過Looper.loop(),來開啟消息循環(huán)梧税,在run()
? ? ? 方法中執(zhí)行耗時操作沦疾,Handler內(nèi)部創(chuàng)建了一個消息隊列,外部需要Handler的方式通知
? ? ? ?HandlerThread執(zhí)行具體的任務(wù)第队,通過quite()/quitSafely()方法來終止線程的執(zhí)行哮塞。
? ? ? 在選擇使用繼承Thread還是實現(xiàn)Runnable時JAVA類可以多實現(xiàn)卻不可以多繼承根據(jù)情況做選擇。
? ? ? 與Runnable的主要區(qū)別是 Callable 的 call() 方法可以返回值和拋出異常凳谦,而 Runnable 的 run() 方法沒 ? ? ? 有這些功能忆畅。Callable 可以返回裝載有計算結(jié)果的 Future 對象。
? ? ?Callable無法在新線程中new Thread(Runnable r)使用晾蜘,Thread 類只支持 Runnable不過 Callable 可以 ? ? ? 使用 ExecutorService 邻眷。
Callable創(chuàng)建線程的兩種方式
1、通過實現(xiàn)Callable接口來創(chuàng)建Thread線程:
步驟1:創(chuàng)建實現(xiàn)Callable接口的類SomeCallable剔交;
步驟2:創(chuàng)建一個類對象:
Callable oneCallable = new SomeCallable();
步驟3:由Callable創(chuàng)建一個FutureTask對象:
FutureTask oneTask = new FutureTask(oneCallable);
FutureTask是一個包裝器肆饶,它通過接受Callable來創(chuàng)建,它同時實現(xiàn)了Future和Runnable接口岖常,他的run方法中實際上會調(diào)用oneCallable.call()驯镊。
步驟4:由FutureTask創(chuàng)建一個Thread對象:
Thread oneThread = new Thread(oneTask);
步驟5:啟動線程:
oneThread.start();
事例:
Callable callable =newCallable() {? ?
?@Override
publicStringcall() throws Exception {
return"哈哈哈";??
? }};
FutureTask task =newFutureTask(callable);
Thread t =newThread(task);t.start();// 啟動線程t
ask.cancel(true);// 取消線程
2、通過線程池來創(chuàng)建線程:
步驟1:創(chuàng)建線程池:
ExecutorService pool = Executors.newCachedThreadPool();
步驟2:通過Runnable對象或Callable對象將任務(wù)提交給ExecutorService對象:
Future future = pool.submit(new Callable()?);竭鞍。
Future
該接口定義5個方法
boolean cancel(boolean mayInterruptIfRunning):試圖取消對此任務(wù)的執(zhí)行板惑。如果任務(wù)已完成、或已取消偎快,或者由于某些其他原因而無法取消冯乘,則此嘗試將失敗。當調(diào)用 cancel() 時晒夹,如果調(diào)用成功裆馒,而此任務(wù)尚未啟動姊氓,則此任務(wù)將永不運行。如果任務(wù)已經(jīng)啟動喷好,則 mayInterruptIfRunning 參數(shù)確定是否應該以試圖停止任務(wù)的方式來中斷執(zhí)行此任務(wù)的線程翔横。此方法返回后,對 isDone() 的后續(xù)調(diào)用將始終返回 true梗搅。如果此方法返回 true禾唁,則對 isCancelled() 的后續(xù)調(diào)用將始終返回 true。
boolean isCancelled():如果在任務(wù)正常完成前將其取消无切,則返回 true荡短。
boolean isDone():如果任務(wù)已完成,則返回 true订雾。 可能由于正常終止肢预、異趁矗或取消而完成洼哎,在所有這些情況中,此方法都將返回 true沼本。
V get()throws InterruptedException,ExecutionException:如有必要噩峦,等待計算完成,然后獲取其結(jié)果抽兆。
V get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException: 如有必要识补,最多等待為使計算完成所給定的時間之后,獲取其結(jié)果(如果結(jié)果可用)
FutureTask
FutureTask實現(xiàn)了兩個接口辫红,Runnable和Future,所以它既可以作為Runnable被線程執(zhí)行,又可以作為Future得到Callable的返回值尿这。
public classFutureTaskimplementsRunnableFuture?{
.........
}
run()方法調(diào)用了callable中的call()
importjava.util.concurrent.*;
publicclassTest?{
public static void main(String[]?args) throwsInterruptedException,
ExecutionException?{
finalExecutorService?exec?=?Executors.newFixedThreadPool(5);
Callable?call?=newCallable()?{
publicString?call()throwsException?{
Thread.sleep(1000*10);//休眠指定的時間只磷,此處表示該操作比較耗時
return"Other?less?important?but?longtime?things.";
}
};
Future?task?=?exec.submit(call);
//重要的事情
System.out.println("Let's?do?important?things.?start");
Thread.sleep(1000*3);
System.out.println("Let's?do?important?things.?end");
//不重要的事情
while(!?task.isDone()){
System.out.println("still?waiting....");
Thread.sleep(1000*1);
}
System.out.println("get?sth....");
String?obj?=?task.get();
System.out.println(obj);
//關(guān)閉線程池
exec.shutdown();
}
}
輸出結(jié)果:
Let's do important things. start
Let's do important things. end
still waiting....
still waiting....
still waiting....
still waiting....
still waiting....
still waiting....
still waiting....
get sth....
Other less important but longtime things.
多線程的特點
1)適當?shù)奶岣叱绦虻膱?zhí)行效率(多個線程同時執(zhí)行)。
2)適當?shù)奶岣吡速Y源利用率(CPU名惩、內(nèi)存等)澎胡。
1)占用一定的內(nèi)存空間。
2)線程越多CPU的調(diào)度開銷越大娩鹉。
3)程序的復雜度會上升攻谁。
synchronized
修飾一個代碼塊,修飾一個方法弯予,修改一個靜態(tài)的方法戚宦,修飾一個類。
wait()锈嫩,notify()受楼,notifyAll()困檩,調(diào)用這三個方法中任意一個,當前線程必須是鎖的持有者那槽,如果不是會拋出一個 IllegalMonitorStateException 異常悼沿。
wait() 方法使實體所處線程暫停執(zhí)行,從而使對象進入等待狀態(tài)骚灸,直到被 notify() 方法通知或者 wait() 的等待的時間到糟趾。
sleep() 方法使持有的線程暫停運行,從而使線程進入休眠狀態(tài)甚牲,直到用 interrupt 方法來打斷他的休眠或者 sleep 的休眠的時間到义郑。
wait() 方法進入等待狀態(tài)時會釋放同步鎖,而 sleep() 方法不會釋放同步鎖丈钙。
thread.Join把指定的線程加入到當前線程非驮,可以將兩個交替執(zhí)行的線程合并為順序執(zhí)行的線程。比如在線程B中調(diào)用了線程A的Join()方法雏赦,直到線程A執(zhí)行完畢后劫笙,才會繼續(xù)執(zhí)行線程B。
Thread.yield():線程放棄運行星岗,將CPU的控制權(quán)讓出填大。
線程池
1)避免線程的創(chuàng)建和銷毀帶來的性能開銷。
2)避免大量的線程間因互相搶占系統(tǒng)資源導致的阻塞現(xiàn)象俏橘。
3}能夠?qū)€程進行簡單的管理并提供定時執(zhí)行允华、間隔執(zhí)行等功能。
Java里面線程池的頂級接口是 Executor寥掐,不過真正的線程池接口是 ExecutorService靴寂, ExecutorService 的默認實現(xiàn)是 ThreadPoolExecutor;
普通類 Executors 里面調(diào)用的就是 ThreadPoolExecutor召耘。
publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,? ? ? ? ? ? ? ? ? ? ? TimeUnit unit, BlockingQueue workQueue, ? ThreadFactory threadFactory){
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);}
1)corePoolSize:線程池的核心線程數(shù)百炬,一般情況下不管有沒有任務(wù)都會一直在線程池中一直存活,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true 時怎茫,閑置的核心線程會存在超時機制收壕,如果在指定時間沒有新任務(wù)來時,核心線程也會被終止轨蛤,而這個時間間隔由第3個屬性 keepAliveTime 指定蜜宪。
2)maximumPoolSize:線程池所能容納的最大線程數(shù),當活動的線程數(shù)達到這個值后祥山,后續(xù)的新任務(wù)將會被阻塞圃验。
3)keepAliveTime:控制線程閑置時的超時時長,超過則終止該線程缝呕。一般情況下用于非核心線程澳窑,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true時斧散,也作用于核心線程。
4)unit:用于指定 keepAliveTime 參數(shù)的時間單位摊聋,TimeUnit 是個 enum 枚舉類型鸡捐,常用的有:TimeUnit.HOURS(小時)、TimeUnit.MINUTES(分鐘)麻裁、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等箍镜。
5)workQueue:線程池的任務(wù)隊列,通過線程池的 execute(Runnable command) 方法會將任務(wù) Runnable 存儲在隊列中煎源。
6)threadFactory:線程工廠色迂,它是一個接口,用來為線程池創(chuàng)建新線程的手销。
Executors提供四種線程池
newCachedThreadPool 可變尺寸的線程池歇僧,但是在以前構(gòu)造的線程可用時將重用它們。對于執(zhí)行很多短期異步任務(wù)的程序而言锋拖,這些線程池通痴┖罚可提高程序性能。調(diào)用 execute() 將重用以前構(gòu)造的線程(如果線程可用)姑隅。如果現(xiàn)有線程沒有可用的写隶,則創(chuàng)建一個新線程并添加到池中倔撞。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程讲仰。因此,長時間保持空閑的線程池不會使用任何資源痪蝇。注意鄙陡,可以使用 ThreadPoolExecutor 構(gòu)造方法創(chuàng)建具有類似屬性但細節(jié)不同(例如超時參數(shù))的線程池。(只有非核心線程躏啰,最大線程數(shù)非常大趁矾,所有線程都活動時,會為新任務(wù)創(chuàng)建新線程给僵,否則利用空閑線程(60s空閑時間毫捣,過了就會被回收,所以線程池中有0個線程的可能)處理任務(wù))
newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池帝际,它只會用唯一的工作線程來執(zhí)行任務(wù)蔓同,所有的任務(wù)是串行執(zhí)行的,如果這個唯一的線程因為異常結(jié)束蹲诀,那么會有一個新的線程來替代它斑粱,保證所有任務(wù)按照指定順序(FIFO)執(zhí)行。(只有一個核心線程脯爪,確保所有任務(wù)都在同一線程中按順序完成则北。因此不需要處理線程同步的問題矿微。)
newFixedThreadPool 固定大小的線程池,每次提交一個任務(wù)就創(chuàng)建一個線程尚揣,直到線程達到線程池的最大大小涌矢,線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執(zhí)行異常而結(jié)束快骗,那么線程池會補充一個新線程蒿辙。(只有核心線程,并且數(shù)量固定的滨巴,也不會被回收思灌,所有線程都活動時,因為隊列沒有限制大小恭取,新任務(wù)會等待執(zhí)行泰偿。)
newScheduledThreadPool 創(chuàng)建一個大小無限的線程池,此線程池支持定時以及周期性執(zhí)行任務(wù)的需求蜈垮。
線程池 submit 和 execute
接收的參數(shù)不一樣
submit有返回值耗跛,而execute沒有
線程池的關(guān)閉
ThreadPoolExecutor 提供了兩個方法,用于線程池的關(guān)閉攒发。
shutdown():不會立即的終止線程池调塌,而是要等所有任務(wù)緩存隊列中的任務(wù)都執(zhí)行完后才終止,但再也不會接受新的任務(wù)惠猿。
shutdownNow():立即終止線程池羔砾,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊列偶妖,返回尚未執(zhí)行的任務(wù)姜凄。