引言
在前幾年小編寫過一篇關於線程池的總結:《線程總結》,現在回過頭來看,總結的 還是比較詳細的,不過當時並沒有在項目中有過真實刺激的 體驗,最近項目中偶然遇到了一次任務丟失的問題,我追蹤了一下 代碼, 發現由於不正當採用java內置線程池導致的, 應該是當時配置線程池的參數沒有仔細計算導致的,關於這個問題我們後面博文在介紹,今天我們在看我java 內置 線程池代碼以後,我們先動手自己寫一個線程池來實現任務的提交和執行。這樣我們可以更好的理解線程池的執行流程。如果讀者對於java內置的線程的核心參數和執行流程不是很瞭解,可以點擊上面鏈接,閱讀博文。
一、在編寫代碼之前,我們先介紹幾個核心參數的配置依據。
1.1、核心線程數量corePoolSize
核心線程數的設計需要根據任務的處理時間和每秒產生的任務數量來確定,例如執行一個任務需要0.1秒,系統百分之八十的時間沒秒都會產生100個任務,那麼我們想要在1秒內處理完這100個任務,就需要10個線程,此時我們就可以設計核心線程數量爲10,當時實際情況不可能這麼平均,所以一般我們按照2080原則設計即可,即按照百分之80的情況設計核心線程數量,剩下的百分之20可以利用最大線程數量處理。
1.2、任務隊列長度(workQueue)
任務隊列長度一般設計爲核心線程數/單個任務執行時間*2(任務最大等待時間/s)即可,例如上面場景中,核心線程數設計爲10,單個任務執行時間爲0.1,則隊列長度可以設計爲200
1.3、最大線程數(maximumPoolSize)
最大線程數的設計除了需要參照核心線程數的條件外,還需要參照系統每秒產生的最大任務數決定,例如,上述環境中,如果系統每秒最大產生的任務數量是1000個,那麼最大線程數=(最大任務數-任務隊列長度)* 單個任務執行時間;既最大線程數=(1000-200)* 0.1 =80;當然最大線程數和服務器的硬件配置也有很大關係
上面的 參數配置公式,都是參考作用,在實際環境中,需要根據實際服務器的配置自行調整
二、定義線程池
==========================開始coding====================================
2.1、模擬任務類
package com.threadpoll;
/**
* @author zhenghao
* @description:
* @date 2020/6/3010:07
*/
public class MyTask implements Runnable {
private int id;
public MyTask(int id) {
this.id = id;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("線程:" + name + "即將執行任務:" + id);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程:" + name + "完成了任務:" + id);
}
@Override
public String toString() {
return "MyTask{" +
"id=" + id +
'}';
}
}
2.2、自定義線程類
package com.threadpoll;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhenghao
* @description:
* @date 2020/6/3010:13
*/
public class MyWorker extends Thread {
private String name;
private List<Runnable> tasks = new ArrayList<>();
public MyWorker(String name, List<Runnable> tasks) {
super(name);
this.tasks = tasks;
}
@Override
public void run() {
while (tasks.size() > 0) {
Runnable r = tasks.remove(0);
r.run();
}
}
}
2.3、自定義線程池類
package com.threadpoll;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* @author zhenghao
* @description: 自定義線程池
* @date 2020/6/3015:06
*/
public class MyThreadPool {
/**
* 任務集合 多個線程同時remove
*/
private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
/**
* 當前先測試數量
*/
private int num;
/**
* 核心線程數量
*/
private int coreThreadSize;
/**
* 最大線程數量
*/
private int maxThreadSize;
/**
* 隊列長度
*/
private int workSize;
public MyThreadPool( int coreThreadSize, int maxThreadSize, int workSize) {
this.coreThreadSize = coreThreadSize;
this.maxThreadSize = maxThreadSize;
this.workSize = workSize;
}
/**
* @Description: 提交隊列
* @author: zhenghao
* @date: 2020/6/30 15:11
*/
public void submitTask(Runnable r) {
if (tasks.size() >= workSize) {
System.out.println("任務" + r + "丟掉了");
} else {
//加入隊列
tasks.add(r);
//執行隊列
execTask(r);
}
}
private void execTask(Runnable r) {
//判斷是否需要創建核心線程池
if (num < coreThreadSize) {
//創建核心線程池執行
new MyWorker("核心線程池" + num, tasks).start();
num++;
} else if (num < maxThreadSize) {
//創建非核心線程池執行
new MyWorker("非核心線程池" + num, tasks).start();
num++;
} else {
System.out.println("任務" + r + "被緩存了");
}
}
}
2.4、測試類
package com.threadpoll;
/**
* @author zhenghao
* @description:
* @date 2020/6/3010:07
*/
public class MyTask implements Runnable {
private int id;
public MyTask(int id) {
this.id = id;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("線程:" + name + "即將執行任務:" + id);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程:" + name + "完成了任務:" + id);
}
@Override
public String toString() {
return "MyTask{" +
"id=" + id +
'}';
}
}
大家通過調解任務數量,也就是測試類中的循環數量,可以看到不同的效果,大家可以測試一下, 然後通過這個小demo,我們可以更好的瞭解線程池的執行流程。