java實(shí)現(xiàn)手寫(xiě)一個(gè)簡(jiǎn)單版的線程池
有些人可能對(duì)線程池比較陌生,并且更不熟悉線程池的工作原理。所以他們?cè)谑褂镁€程的時(shí)候,多數(shù)情況下都是new Thread來(lái)實(shí)現(xiàn)多線程。但是,往往良好的多線程設(shè)計(jì)大多都是使用線程池來(lái)實(shí)現(xiàn)的。 為什么要使用線程 降低資源的消耗。降低線程創(chuàng)建和銷(xiāo)毀的資源消耗。提高響應(yīng)速度:線程的創(chuàng)建時(shí)間為T(mén)1,執(zhí)行時(shí)間T2,銷(xiāo)毀時(shí)間T3,免去T1和T3的時(shí)間提高線程的可管理性
下圖所示為線程池的實(shí)現(xiàn)原理:調(diào)用方不斷向線程池中提交任務(wù);線程池中有一組線程,不斷地從隊(duì)列中取任務(wù),這是一個(gè)典型的生產(chǎn)者-消費(fèi)者模型。

要實(shí)現(xiàn)一個(gè)線程池,有幾個(gè)問(wèn)題需要考慮:
- 隊(duì)列設(shè)置多長(zhǎng)?如果是無(wú)界的,調(diào)用方不斷往隊(duì)列中方任務(wù),可能導(dǎo)致內(nèi)存耗盡。如果是有界的,當(dāng)隊(duì)列滿了之后,調(diào)用方如何處理?
- 線程池中的線程個(gè)數(shù)是固定的,還是動(dòng)態(tài)變化的?
- 每次提交新任務(wù),是放入隊(duì)列?還是開(kāi)新線程
- 當(dāng)沒(méi)有任務(wù)的時(shí)候,線程是睡眠一小段時(shí)間?還是進(jìn)入阻塞?如果進(jìn)入阻塞,如何喚醒?
針對(duì)問(wèn)題4,有3種做法:
- 不使用阻塞隊(duì)列,只使用一般的線程安全的隊(duì)列,也無(wú)阻塞/喚醒機(jī)制。當(dāng)隊(duì)列為空時(shí),線程池中的線程只能睡眠一會(huì)兒,然后醒來(lái)去看隊(duì)列中有沒(méi)有新任務(wù)到來(lái),如此不斷輪詢(xún)。
- 不使用阻塞隊(duì)列,但在隊(duì)列外部,線程池內(nèi)部實(shí)現(xiàn)了阻塞/喚醒機(jī)制
- 使用阻塞隊(duì)列
很顯然,做法3最完善,既避免了線程池內(nèi)部自己實(shí)現(xiàn)阻塞/喚醒機(jī)制的麻煩,也避免了做法1的睡眠/輪詢(xún)帶來(lái)的資源消耗和延遲。
現(xiàn)在來(lái)帶大家手寫(xiě)一個(gè)簡(jiǎn)單的線程池,讓大家更加理解線程池的工作原理
實(shí)戰(zhàn):手寫(xiě)簡(jiǎn)易線程池
根據(jù)上圖可以知道,實(shí)現(xiàn)線程池需要一個(gè)阻塞隊(duì)列+存放線程的容器
/**
* Five在努力
* 自定義線程池
*/
public class ThreadPool {
/** 默認(rèn)線程池中的線程的數(shù)量 */
private static final int WORK_NUM = 5;
/** 默認(rèn)處理任務(wù)的數(shù)量 */
private static final int TASK_NUM = 100;
/** 存放任務(wù) */
private final BlockingQueue<Runnable> taskQueue;
private final Set<WorkThread> workThreads;//保存線程的集合
private int workNumber;//線程數(shù)量
private int taskNumber;//任務(wù)數(shù)量
public ThreadPool(){
this(WORK_NUM , TASK_NUM);
}
public ThreadPool(int workNumber , int taskNumber) {
if (taskNumber<=0){
taskNumber = TASK_NUM;
}
if (workNumber<=0){
workNumber = WORK_NUM;
}
this.taskQueue = new ArrayBlockingQueue<Runnable>(taskNumber);
this.workNumber = workNumber;
this.taskNumber = taskNumber;
workThreads = new HashSet<>();
//工作線程準(zhǔn)備好了
//啟動(dòng)一定數(shù)量的線程數(shù),從隊(duì)列中獲取任務(wù)處理
for (int i=0;i<workNumber;i++) {
WorkThread workThread = new WorkThread("thead_"+i);
workThread.start();
workThreads.add(workThread);
}
}
/**
* 線程池執(zhí)行任務(wù)的方法,其實(shí)就是往BlockingQueue中添加元素
* @param task
*/
public void execute(Runnable task) {
try {
taskQueue.put(task);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 銷(xiāo)毀線程池
*/
public void destroy(){
System.out.println("ready close pool...");
for (WorkThread workThread : workThreads) {
workThread.stopWorker();
workThread = null;//help gc
}
workThreads.clear();
}
/** 內(nèi)部類(lèi),工作線程的實(shí)現(xiàn) */
private class WorkThread extends Thread{
public WorkThread(String name){
super();
setName(name);
}
@Override
public void run() {
while (!interrupted()) {
try {
Runnable runnable = taskQueue.take();//獲取任務(wù)
if (runnable !=null) {
System.out.println(getName()+" ready execute:"+runnable.toString());
runnable.run();//執(zhí)行任務(wù)
}
runnable = null;//help gc
} catch (Exception e) {
interrupt();
e.printStackTrace();
}
}
}
public void stopWorker(){
interrupt();
}
}
}
上面代碼定義了默認(rèn)的線程數(shù)量和默認(rèn)處理任務(wù)數(shù)量,同時(shí)用戶也可以自定義線程數(shù)量和處理任務(wù)數(shù)量。用BlockingQueue阻塞隊(duì)列來(lái)存放任務(wù)。用set來(lái)存放工作線程,set的好處就不用多說(shuō)了。懂的都懂
構(gòu)造方法中new對(duì)象的時(shí)候,循環(huán)啟動(dòng)線程,并把線程放入set中。WorkThread實(shí)現(xiàn)Thread,run方法實(shí)現(xiàn)也很簡(jiǎn)單,因?yàn)橛幸粋€(gè)stop方法,所以這里需要while判斷,之后從taskQueue隊(duì)列中,獲取任務(wù)。如何獲取不到就阻塞,獲取到的話runnable.run();就執(zhí)行任務(wù),之后把任務(wù)變成null
銷(xiāo)毀線程只需要遍歷set,把每個(gè)線程停止,并且變?yōu)閚ull就行了
執(zhí)行線程任務(wù)execute,只需要從往阻塞隊(duì)列中添加任務(wù)就行了
測(cè)試一下:
public class TestMySelfThreadPool {
private static final int TASK_NUM = 50;//任務(wù)的個(gè)數(shù)
public static void main(String[] args) {
ThreadPool myPool = new ThreadPool(3,50);
for (int i=0;i<TASK_NUM;i++) {
myPool.execute(new MyTask("task_"+i));
}
}
static class MyTask implements Runnable{
private String name;
public MyTask(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("task :"+name+" end...");
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "name = "+name;
}
}
}

結(jié)果ok。沒(méi)什么問(wèn)題
到此這篇關(guān)于java實(shí)現(xiàn)手寫(xiě)一個(gè)簡(jiǎn)單版的線程池的文章就介紹到這了,更多相關(guān)java 手寫(xiě)線程池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea創(chuàng)建maven項(xiàng)目速度慢的三種解決方案
這篇文章主要介紹了idea創(chuàng)建maven項(xiàng)目速度慢的三種解決方案,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
3分鐘純 Java 注解搭個(gè)管理系統(tǒng)的示例代碼
這篇文章主要介紹了3分鐘純 Java 注解搭個(gè)管理系統(tǒng)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
利用Lambda表達(dá)式創(chuàng)建新線程案例
這篇文章主要介紹了利用Lambda表達(dá)式創(chuàng)建新線程案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
java虛擬機(jī)原理:Class字節(jié)碼二進(jìn)制文件分析
class文件全名稱(chēng)為Java class文件,主要在平臺(tái)無(wú)關(guān)性和網(wǎng)絡(luò)移動(dòng)性方面使Java更適合網(wǎng)絡(luò)。它在平臺(tái)無(wú)關(guān)性方面的任務(wù)是:為Java程序提供獨(dú)立于底層主機(jī)平臺(tái)的二進(jìn)制形式的服務(wù)。下面我們來(lái)詳細(xì)解讀下它吧2021-09-09
SpringAOP中的動(dòng)態(tài)代理技術(shù)深入解析
這篇文章主要介紹了SpringAOP中的動(dòng)態(tài)代理技術(shù)深入解析,spring默認(rèn)使用JDK動(dòng)態(tài)代理實(shí)現(xiàn)AOP,類(lèi)如果實(shí)現(xiàn)了接口,spring就會(huì)用JDK動(dòng)態(tài)代理實(shí)現(xiàn)AOP,如果目標(biāo)類(lèi)沒(méi)有實(shí)現(xiàn)接口,spring則使用Cglib動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)AOP,需要的朋友可以參考下2024-01-01
java如何連接數(shù)據(jù)庫(kù)executeUpdate()和executeQuery()
這篇文章主要介紹了java如何連接數(shù)據(jù)庫(kù)executeUpdate()和executeQuery(),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

