java編寫屬于自己的線程池
什么是線程池
線程池就是以一個(gè)或多個(gè)線程[循環(huán)執(zhí)行]多個(gè)應(yīng)用邏輯的線程集合.
一般而言,線程池有以下幾個(gè)部分:
完成主要任務(wù)的一個(gè)或多個(gè)線程.
用于調(diào)度管理的管理線程.
要求執(zhí)行的任務(wù)隊(duì)列.
線程池的作用:
線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量。
根據(jù)系統(tǒng)的環(huán)境情況,可以自動(dòng)或手動(dòng)設(shè)置線程數(shù)量,達(dá)到運(yùn)行的最佳效果;少了浪費(fèi)了系統(tǒng)資源,多了造成系統(tǒng)擁擠效率不高。用線程池控制線程數(shù)量,其他線程排隊(duì)等候。一個(gè)任務(wù)執(zhí)行完畢,再?gòu)年?duì)列的中取最前面的任務(wù)開始執(zhí)行。若隊(duì)列中沒有等待進(jìn)程,線程池的這一資源處于等待。當(dāng)一個(gè)新任務(wù)需要運(yùn)行時(shí),如果線程池中有等待的工作線程,就可以開始運(yùn)行了;否則進(jìn)入等待隊(duì)列。
自己實(shí)現(xiàn)線程池
根據(jù)如上對(duì)線程池的理解,我們自己編寫一個(gè)屬于自己的簡(jiǎn)單線程池:
簡(jiǎn)單的線程池接口:
public interface ThreadPool<Job extends Runnable>{
//執(zhí)行一個(gè)任務(wù)(Job),這個(gè)Job必須實(shí)現(xiàn)Runnable
void execute(Job job);
//關(guān)閉線程池
void shutdown();
//增加工作者線程,即用來執(zhí)行任務(wù)的線程
void addWorkers(int num);
//減少工作者線程
void removeWorker(int num);
//獲取正在等待執(zhí)行的任務(wù)數(shù)量
void getJobSize();
}
客戶端可以通過execute(Job)方法將Job提交入線程池來執(zhí)行,客戶端完全不用等待Job的執(zhí)行完成。除了execute(Job)方法以外,線程池接口提供了增加/減少工作者線程以及關(guān)閉線程池的方法。每個(gè)客戶端提交的Job都會(huì)進(jìn)入到一個(gè)工作隊(duì)列中等待工作者線程的處理。
線程池接口的默認(rèn)實(shí)現(xiàn)
public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job>{
// 線程池維護(hù)工作者線程的最大數(shù)量
private static final int MAX_WORKER_NUMBERS = 10;
// 線程池維護(hù)工作者線程的默認(rèn)值
private static final int DEFAULT_WORKER_NUMBERS = 5;
// 線程池維護(hù)工作者線程的最小數(shù)量
private static final int MIN_WORKER_NUMBERS = 1;
// 維護(hù)一個(gè)工作列表,里面加入客戶端發(fā)起的工作
private final LinkedList<Job> jobs = new LinkedList<Job>();
// 工作者線程的列表
private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());
// 工作者線程的數(shù)量
private int workerNum;
// 每個(gè)工作者線程編號(hào)生成
private AtomicLong threadNum = new AtomicLong();
//生成線程池
public DefaultThreadPool() {
this.workerNum = DEFAULT_WORKER_NUMBERS;
initializeWorkers(this.workerNum);
}
public DefaultThreadPool(int num) {
if (num > MAX_WORKER_NUMBERS) {
this.workerNum =DEFAULT_WORKER_NUMBERS;
} else {
this.workerNum = num;
}
initializeWorkers(this.workerNum);
}
//初始化每個(gè)工作者線程
private void initializeWorkers(int num) {
for (int i = 0; i < num; i++) {
Worker worker = new Worker();
//添加到工作者線程的列表
workers.add(worker);
//啟動(dòng)工作者線程
Thread thread = new Thread(worker);
thread.start();
}
}
public void execute(Job job) {
if (job != null) {
//根據(jù)線程的"等待/通知機(jī)制"這里必須對(duì)jobs加鎖
synchronized (jobs) {
jobs.addLast(job);
jobs.notify();
}
}
}
//關(guān)閉線程池即關(guān)閉每個(gè)工作者線程
public void shutdown() {
for (Worker w : workers) {
w.shutdown();
}
}
//增加工作者線程
public void addWorkers(int num) {
//加鎖,防止該線程還么增加完成而下個(gè)線程繼續(xù)增加導(dǎo)致工作者線程超過最大值
synchronized (jobs) {
if (num + this.workerNum > MAX_WORKER_NUMBERS) {
num = MAX_WORKER_NUMBERS - this.workerNum;
}
initializeWorkers(num);
this.workerNum += num;
}
}
//減少工作者線程
public void removeWorker(int num) {
synchronized (jobs) {
if(num>=this.workerNum){
throw new IllegalArgumentException("超過了已有的線程數(shù)量");
}
for (int i = 0; i < num; i++) {
Worker worker = workers.get(i);
if (worker != null) {
//關(guān)閉該線程并從列表中移除
worker.shutdown();
workers.remove(i);
}
}
this.workerNum -= num;
}
}
public int getJobSize() {
// TODO Auto-generated method stub
return workers.size();
}
//定義工作者線程
class Worker implements Runnable {
// 表示是否運(yùn)行該worker
private volatile boolean running = true;
public void run() {
while (running) {
Job job = null;
//線程的等待/通知機(jī)制
synchronized (jobs) {
if (jobs.isEmpty()) {
try {
jobs.wait();//線程等待喚醒
} catch (InterruptedException e) {
//感知到外部對(duì)該線程的中斷操作,返回
Thread.currentThread().interrupt();
return;
}
}
// 取出一個(gè)job
job = jobs.removeFirst();
}
//執(zhí)行job
if (job != null) {
job.run();
}
}
}
// 終止該線程
public void shutdown() {
running = false;
}
}
}
從線程池的實(shí)現(xiàn)中可以看出,當(dāng)客戶端調(diào)用execute(Job)方法時(shí),會(huì)不斷地向任務(wù)列表jobs中添加Job,而每個(gè)工作者線程會(huì)不讀的從jobs上獲取Job來執(zhí)行,當(dāng)jobs為空時(shí),工作者線程進(jìn)入WAITING狀態(tài)。
當(dāng)添加一個(gè)Job后,對(duì)工作隊(duì)列jobs調(diào)用其notify()方法來喚醒一個(gè)工作者線程。此處我們不調(diào)用notifyAll(),避免將等待隊(duì)列中的線程全部移動(dòng)到阻塞隊(duì)列中而造成資源浪費(fèi)。
線程池的本質(zhì)就是使用了一個(gè)線程安全的工作隊(duì)列連接工作者線程和客戶端線程??蛻舳司€程把任務(wù)放入工作隊(duì)列后便返回,而工作者線程則不端的從工作隊(duì)列中取出工作并執(zhí)行。當(dāng)工作隊(duì)列為空時(shí),工作者線程進(jìn)入WAITING狀態(tài),當(dāng)有客戶端發(fā)送任務(wù)過來后會(huì)通過任意一個(gè)工作者線程,隨著大量任務(wù)的提交,更多的工作者線程被喚醒。
參考: 《java并發(fā)編程的藝術(shù)》 方騰飛
相關(guān)文章
Spring Boot的Maven插件Spring Boot Maven plu
Spring Boot的Maven插件Spring Boot Maven plugin以Maven的方式提供Spring Boot支持,Spring Boot Maven plugin將Spring Boot應(yīng)用打包為可執(zhí)行的jar或war文件,然后以通常的方式運(yùn)行Spring Boot應(yīng)用,本文介紹Spring Boot的Maven插件Spring Boot Maven plugin,一起看看吧2024-01-01
JAVA新手學(xué)習(xí)篇之類和對(duì)象詳解
這篇文章主要給大家介紹了關(guān)于JAVA新手學(xué)習(xí)篇之類和對(duì)象的相關(guān)資料,Java是面向?qū)ο蟮木幊陶Z言,主旨在于通過對(duì)象封裝屬性和方法實(shí)現(xiàn)功能,面向?qū)ο笈c面向過程的區(qū)別在于關(guān)注點(diǎn)的不同,需要的朋友可以參考下2024-10-10
RabbitMQ交換機(jī)使用場(chǎng)景和消息可靠性總結(jié)分析
這篇文章主要為大家介紹了RabbitMQ交換機(jī)使用場(chǎng)景和消息可靠性總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
java中JSON字符串轉(zhuǎn)換為Map集合的兩種方法
本文主要介紹了java中JSON字符串轉(zhuǎn)換為Map集合,包含了兩種方法,這種需求可能涉及到從外部接口獲取數(shù)據(jù),或者在程序中處理配置信息等,感興趣的可以了解一下2024-07-07
Springboot Maven打包跳過測(cè)試的五種方式小結(jié)
本文主要介紹了Springboot Maven打包跳過測(cè)試的五種方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
解決springboot項(xiàng)目上傳文件出現(xiàn)臨時(shí)文件目錄為空的問題
這篇文章主要介紹了解決springboot項(xiàng)目上傳文件出現(xiàn)臨時(shí)文件目錄為空的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
java 讀取excel文件轉(zhuǎn)換成json格式的實(shí)例代碼
這篇文章主要介紹了 java 讀取excel文件轉(zhuǎn)換成json格式的實(shí)例代碼,需要的朋友可以參考下2018-04-04

