线程使用二——线程池
線程池的作用:
???? 線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量。
???? 根據(jù)系統(tǒng)的環(huán)境情況,可以自動或手動設(shè)置線程數(shù)量,達(dá)到運(yùn)行的最佳效果;少了浪費(fèi)了系統(tǒng)資源,多了造成系統(tǒng)擁擠效率不高。用線程池控制線程數(shù)量,其他線程 排隊(duì)等候。一個任務(wù)執(zhí)行完畢,再從隊(duì)列的中取最前面的任務(wù)開始執(zhí)行。若隊(duì)列中沒有等待進(jìn)程,線程池的這一資源處于等待。當(dāng)一個新任務(wù)需要運(yùn)行時,如果線程 池中有等待的工作線程,就可以開始運(yùn)行了;否則進(jìn)入等待隊(duì)列。
?
為什么要用線程池:
減少了創(chuàng)建和銷毀線程的次數(shù),每個工作線程都可以被重復(fù)利用,可執(zhí)行多個任務(wù)
可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線線程的數(shù)目,防止因?yàn)橐驗(yàn)橄倪^多的內(nèi)存,而把服務(wù)器累趴下(每個線程需要大約1MB內(nèi)存,線程開的越多,消耗的內(nèi)存也就越大,最后死機(jī))
ThreadGroup與ThreadPoolExecutor的區(qū)別
?
我自己的理解也是一直以為ThreadGroup就是ThreadPoolExecutor(線程池),這是一個非常大的誤會,最近把兩者仔細(xì)分析了下。 線程組表示一個線程的集合。此外,線程組也可以包含其他線程組。線程組構(gòu)成一棵樹,在樹中,除了初始線程組外,每個線程組都有一個父線程組。允許線程訪問 有關(guān)自己的線程組的信息,但是不允許它訪問有關(guān)其線程組的父線程組或其他任何線程組的信息;線程消耗包括內(nèi)存和其它系統(tǒng)資源在內(nèi)的大量資源。除了 Thread 對象所需的內(nèi)存之外,每個線程都需要兩個可能很大的執(zhí)行調(diào)用堆棧。除此以外,JVM 可能會為每個 Java 線程創(chuàng)建一個本機(jī)線程,這些本機(jī)線程將消耗額外的系統(tǒng)資源。最后,雖然線程之間切換的調(diào)度開銷很小,但如果有很多線程,環(huán)境切換也可能嚴(yán)重地影響程序的性 能。線程池是因?yàn)榫€程的生成關(guān)閉很浪費(fèi)資源 所以不要頻繁的操作 線程次 就是管理線程的地方 不用了它可以讓它休眠也就是他替你管理線程 而且比你管理的要好的多。線程池為線程生命周期開銷問題和資源不足問題提供了解決方案。通過對多個任務(wù)重用線程,線程創(chuàng)建的開銷被分?jǐn)偟搅硕鄠€任務(wù)上。其 好處是,因?yàn)樵谡埱蟮竭_(dá)時線程已經(jīng)存在,所以無意中也消除了線程創(chuàng)建所帶來的延遲。這樣,就可以立即為請求服務(wù),使應(yīng)用程序響應(yīng)更快。而且,通過適當(dāng)?shù)卣{(diào) 整線程池中的線程數(shù)目,也就是當(dāng)請求的數(shù)目超過某個閾值時,就強(qiáng)制其它任何新到的請求一直等待,直到獲得一個線程來處理為止,從而可以防止資源不足。
?
Executor詳解:
?
Java里面線程池的頂級接口是Executor,但是嚴(yán)格意義上講Executor并不是一個線程池,而只是一個執(zhí)行線程的工具。真正的線程池接口是ExecutorService。ThreadPoolExecutor是Executors類的底層實(shí)現(xiàn)。我們先介紹下Executors。
?
Sun在Java5中,對Java線程的類庫做了大量的擴(kuò)展,其中線程池就是Java5的新特征之一,除了線程池之外,還有很多多線程相關(guān)的內(nèi)容,為多線程的編程帶來了極大便利。為了編寫高效穩(wěn)定可靠的多線程程序,線程部分的新增內(nèi)容顯得尤為重要。?
有關(guān)Java5線程新特征的內(nèi)容全部在java.util.concurrent下面,里面包含數(shù)目眾多的接口和類,熟悉這部分API特征是一項(xiàng)艱難的學(xué)習(xí)過程。目前有關(guān)這方面的資料和書籍都少之又少,大所屬介紹線程方面書籍還停留在java5之前的知識層面上。?
當(dāng)然新特征對做多線程程序沒有必須的關(guān)系,在java5之前通用可以寫出很優(yōu)秀的多線程程序。只是代價不一樣而已。?
線程池的基本思想還是一種對象池的思想,開辟一塊內(nèi)存空間,里面存放了眾多(未死亡)的線程,池中線程執(zhí)行調(diào)度由池管理器來處理。當(dāng)有線程任務(wù)時,從池中取一個,執(zhí)行完成后線程對象歸池,這樣可以避免反復(fù)創(chuàng)建線程對象所帶來的性能開銷,節(jié)省了系統(tǒng)的資源。?
在Java5之前,要實(shí)現(xiàn)一個線程池是相當(dāng)有難度的,現(xiàn)在Java5為我們做好了一切,我們只需要按照提供的API來使用,即可享受線程池帶來的極大便利。?
Java5的線程池分好多種:固定尺寸的線程池、可變尺寸連接池。?
在使用線程池之前,必須知道如何去創(chuàng)建一個線程池,在Java5中,需要了解的是java.util.concurrent.Executors類的API,這個類提供大量創(chuàng)建連接池的靜態(tài)方法,是必須掌握的。
?
實(shí)例:
?
一、固定大小的線程池?
Java代碼??
import?java.util.concurrent.Executors;??
import?java.util.concurrent.ExecutorService;??
/**?
*?Java線程:線程池-?
*?
*?@author?Administrator?2009-11-4?23:30:44?
*/??
public?class?Test?{??
public?static?void?main(String[]?args)?{??
//創(chuàng)建一個可重用固定線程數(shù)的線程池??
ExecutorService?pool?=?Executors.newFixedThreadPool(2);??
//創(chuàng)建實(shí)現(xiàn)了Runnable接口對象,Thread對象當(dāng)然也實(shí)現(xiàn)了Runnable接口??
Thread?t1?=?new?MyThread();??
Thread?t2?=?new?MyThread();??
Thread?t3?=?new?MyThread();??
Thread?t4?=?new?MyThread();??
Thread?t5?=?new?MyThread();??
//將線程放入池中進(jìn)行執(zhí)行??
pool.execute(t1);??
pool.execute(t2);??
pool.execute(t3);??
pool.execute(t4);??
pool.execute(t5);??
//關(guān)閉線程池??
pool.shutdown();??
}??
}??
class?MyThread?extends?Thread{??
@Override??
public?void?run()?{??
System.out.println(Thread.currentThread().getName()+"正在執(zhí)行。。。");??
}??
}???
?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-2正在執(zhí)行。。。?
Process finished with exit code 0?
二、單任務(wù)線程池?
在上例的基礎(chǔ)上改一行創(chuàng)建pool對象的代碼為:?
//創(chuàng)建一個使用單個 worker 線程的 Executor,以×××隊(duì)列方式來運(yùn)行該線程。?
Java代碼??
ExecutorService?pool?=?Executors.newSingleThreadExecutor();???
?
輸出結(jié)果為:?
????? pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
Process finished with exit code 0?
對于以上兩種連接池,大小都是固定的,當(dāng)要加入的池的線程(或者任務(wù))超過池最大尺寸時候,則入此線程池需要排隊(duì)等待。?
一旦池中有線程完畢,則排隊(duì)等待的某個線程會入池執(zhí)行。?
三、可變尺寸的線程池?
與上面的類似,只是改動下pool的創(chuàng)建方式:?
//創(chuàng)建一個可根據(jù)需要創(chuàng)建新線程的線程池,但是在以前構(gòu)造的線程可用時將重用它們。?
Java代碼??
ExecutorService?pool?=?Executors.newCachedThreadPool();???
?
pool-1-thread-5正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-4正在執(zhí)行。。。?
pool-1-thread-3正在執(zhí)行。。。?
pool-1-thread-2正在執(zhí)行。。。?
Process finished with exit code 0?
四、延遲連接池
?
Java代碼??
import?java.util.concurrent.Executors;??
import?java.util.concurrent.ScheduledExecutorService;??
import?java.util.concurrent.TimeUnit;??
/**?
*?Java線程:線程池-?
*?
*?@author?Administrator?2009-11-4?23:30:44?
*/??
public?class?Test?{??
public?static?void?main(String[]?args)?{??
//創(chuàng)建一個線程池,它可安排在給定延遲后運(yùn)行命令或者定期地執(zhí)行。??
ScheduledExecutorService?pool?=?Executors.newScheduledThreadPool(2);??
//創(chuàng)建實(shí)現(xiàn)了Runnable接口對象,Thread對象當(dāng)然也實(shí)現(xiàn)了Runnable接口??
Thread?t1?=?new?MyThread();??
Thread?t2?=?new?MyThread();??
Thread?t3?=?new?MyThread();??
Thread?t4?=?new?MyThread();??
Thread?t5?=?new?MyThread();??
//將線程放入池中進(jìn)行執(zhí)行??
pool.execute(t1);??
pool.execute(t2);??
pool.execute(t3);??
//使用延遲執(zhí)行風(fēng)格的方法??
pool.schedule(t4,?10,?TimeUnit.MILLISECONDS);??
pool.schedule(t5,?10,?TimeUnit.MILLISECONDS);??
//關(guān)閉線程池??
pool.shutdown();??
}??
}??
class?MyThread?extends?Thread?{??
@Override??
public?void?run()?{??
System.out.println(Thread.currentThread().getName()?+?"正在執(zhí)行。。。");??
}??
}???
?
?? pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-2正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-2正在執(zhí)行。。。?
Process finished with exit code 0?
五、單任務(wù)延遲連接池?
在四代碼基礎(chǔ)上,做改動?
//創(chuàng)建一個單線程執(zhí)行程序,它可安排在給定延遲后運(yùn)行命令或者定期地執(zhí)行。?
?
Java代碼??
ScheduledExecutorService?pool?=?Executors.newSingleThreadScheduledExecutor();???
?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
Process finished with exit code 0?
六、自定義線程池?
?
Java代碼??
import?java.util.concurrent.ArrayBlockingQueue;??
import?java.util.concurrent.BlockingQueue;??
import?java.util.concurrent.ThreadPoolExecutor;??
import?java.util.concurrent.TimeUnit;??
/**?
*?Java線程:線程池-自定義線程池?
*?
*?@author?Administrator?2009-11-4?23:30:44?
*/??
public?class?Test?{??
public?static?void?main(String[]?args)?{??
//創(chuàng)建等待隊(duì)列??
BlockingQueue?bqueue?=?new?ArrayBlockingQueue(20);??
//創(chuàng)建一個單線程執(zhí)行程序,它可安排在給定延遲后運(yùn)行命令或者定期地執(zhí)行。??
ThreadPoolExecutor?pool?=?new?ThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bqueue);??
//創(chuàng)建實(shí)現(xiàn)了Runnable接口對象,Thread對象當(dāng)然也實(shí)現(xiàn)了Runnable接口??
Thread?t1?=?new?MyThread();??
Thread?t2?=?new?MyThread();??
Thread?t3?=?new?MyThread();??
Thread?t4?=?new?MyThread();??
Thread?t5?=?new?MyThread();??
Thread?t6?=?new?MyThread();??
Thread?t7?=?new?MyThread();??
//將線程放入池中進(jìn)行執(zhí)行??
pool.execute(t1);??
pool.execute(t2);??
pool.execute(t3);??
pool.execute(t4);??
pool.execute(t5);??
pool.execute(t6);??
pool.execute(t7);??
//關(guān)閉線程池??
pool.shutdown();??
}??
}??
class?MyThread?extends?Thread?{??
@Override??
public?void?run()?{??
System.out.println(Thread.currentThread().getName()?+?"正在執(zhí)行。。。");??
try?{??
Thread.sleep(100L);??
}?catch?(InterruptedException?e)?{??
e.printStackTrace();??
}??
}??
}???
?
??
????? pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-2正在執(zhí)行。。。?
pool-1-thread-2正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-2正在執(zhí)行。。。?
pool-1-thread-1正在執(zhí)行。。。?
pool-1-thread-2正在執(zhí)行。。。?
Process finished with exit code 0?
創(chuàng)建自定義線程池的構(gòu)造方法很多,本例中參數(shù)的含義如下:?
?? ThreadPoolExecutor?
public ThreadPoolExecutor(int corePoolSize,?int maximumPoolSize,?long keepAliveTime,?TimeUnit unit,? BlockingQueue workQueue,RejectedExecutionHandler handler)?
用給定的初始參數(shù)和默認(rèn)的線程工廠及處理程序創(chuàng)建新的 ThreadPoolExecutor。使用 Executors 工廠方法之一比使用此通用構(gòu)造方法方便得多。?
參數(shù):?
corePoolSize - 池中所保存的線程數(shù),包括空閑線程。?
maximumPoolSize - 池中允許的最大線程數(shù)。?
keepAliveTime - 當(dāng)線程數(shù)大于核心時,此為終止前多余的空閑線程等待新任務(wù)的最長時間。?
unit - keepAliveTime 參數(shù)的時間單位。?
workQueue - 執(zhí)行前用于保持任務(wù)的隊(duì)列。此隊(duì)列僅保持由 execute 方法提交的 Runnable 任務(wù)。?
????????
???? ??RejectedExecutionHandler?
????????此處需要說明一下:當(dāng)池子里的線程數(shù)少于corePoolSize的個數(shù)時就會創(chuàng)建新的線程,即使有空閑的線程也會新建線程去執(zhí)行任務(wù)直到等于corePoolSize的個數(shù),當(dāng)這時如果又有新的任務(wù)進(jìn)來這些任務(wù)就會進(jìn)入workQueue中進(jìn)行等待,當(dāng)有空閑的線程時就會從workQueue去取任務(wù)執(zhí)行,如果workQueue中的任務(wù)也放滿了并且maximumPoolSize大于
corePoolSize,此時就會在創(chuàng)建新的線程去調(diào)用任務(wù)(只有workQueue中的任務(wù)滿了且maximumPoolSize大于corePoolSize時才會新建線程,而不是corePoolSize不夠就直接去新建線程直到個數(shù)=maximumPoolSize),如果這樣還不滿足任務(wù)個數(shù)的需求,接下來的任務(wù)就去調(diào)用RejectedExecutionHandler。
也就是:處理任務(wù)的優(yōu)先級為:?
核心線程corePoolSize、任務(wù)隊(duì)列workQueue、最大線程maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務(wù)。?
unit可選的參數(shù)為java.util.concurrent.TimeUnit中的幾個靜態(tài)屬性:?
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。?
workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue?
handler有五個選擇:?
ThreadPoolExecutor.AbortPolicy()?
拋出java.util.concurrent.RejectedExecutionException異常?
ThreadPoolExecutor.CallerRunsPolicy()?
重試添加當(dāng)前的任務(wù),他會自動重復(fù)調(diào)用execute()方法?
ThreadPoolExecutor.DiscardOldestPolicy()?
拋棄舊的任務(wù)?
ThreadPoolExecutor.DiscardPolicy()?
拋棄當(dāng)前的任務(wù)?
還有一種是實(shí)現(xiàn)RejectedExecutionHandler接口重新方法
拋出:?
IllegalArgumentException - 如果 corePoolSize 或 keepAliveTime 小于零,或者 maximumPoolSize 小于或等于零,或者 corePoolSize 大于 maximumPoolSize。?
NullPointerException - 如果 workQueue 為 null?
自定義連接池稍微麻煩些,不過通過創(chuàng)建的ThreadPoolExecutor線程池對象,可以獲取到當(dāng)前線程池的尺寸、正在執(zhí)行任務(wù)的線程數(shù)、工作隊(duì)列等等。
轉(zhuǎn)載于:https://blog.51cto.com/11745766/1839689
總結(jié)
以上是生活随笔為你收集整理的线程使用二——线程池的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 09Mybatis_入门程序——删除用户
- 下一篇: Swift 3.0 beta 6权限访问