池化是常見的思想,線程池是非常典型的池化的實現,《Java併發編程實戰》也大篇幅去講解了Java中的線程池。本文實現一個簡單的線程池。
2 核心類
【1】接口定義
public interface IThreadPool<Job extends Runnable> {
/**
* 關閉線程池
*/
public void shutAlldown();
/**
* 執行任務
*
* @param job 任務
*/
public void execute(Job job);
/**
* 添加工作者
*
* @param addNum 添加數
*/
public void addWorkers(int addNum);
/**
* 減少工作者
*
* @param reduceNum 減少數目
*/
public void reduceWorkers(int reduceNum);
}
【2】實現類
線程池的核心是維護了1個任務列表和1個工作者列表。
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class XYThreadPool<Job extends Runnable> implements IThreadPool<Job> {
// 默認線程數
private static int DEAFAULT_SIZE = 5;
// 最大線程數
private static int MAX_SIZE = 10;
// 任務列表
private LinkedList<Job> tasks = new LinkedList<Job>();
// 工作線程列表
private List<Worker> workers = Collections
.synchronizedList(new ArrayList<Worker>());
/**
* 默認構造函數
*/
public XYThreadPool() {
initWokers(DEAFAULT_SIZE);
}
/**
* 執行線程數
*
* @param threadNums 線程數
*/
public XYThreadPool(int workerNum) {
workerNum = workerNum <= 0 ? DEAFAULT_SIZE
: workerNum > MAX_SIZE ? MAX_SIZE : workerNum;
initWokers(workerNum);
}
/**
* 初始化線程池
*
* @param threadNums 線程數
*/
public void initWokers(int threadNums) {
for (int i = 0; i < threadNums; i++) {
Worker worker = new Worker();
worker.start();
workers.add(worker);
}
// 添加關閉鉤子
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
shutAlldown();
}
});
}
@Override
public void shutAlldown() {
for (Worker worker : workers) {
worker.shutdown();
}
}
@Override
public void execute(Job job) {
synchronized (tasks) {
// 提交任務就是將任務對象加入任務隊列,等待工作線程去處理
tasks.addLast(job);
tasks.notifyAll();
}
}
@Override
public void addWorkers(int addNum) {
// 新線程數必須大於零,並且線程總數不能大於最大線程數
if ((workers.size() + addNum) <= MAX_SIZE && addNum > 0) {
initWokers(addNum);
} else {
System.out.println("addNum too large");
}
}
@Override
public void reduceWorkers(int reduceNum) {
if ((workers.size() - reduceNum <= 0))
System.out.println("thread num too small");
else {
// 暫停指定數量的工作者
int count = 0;
while (count != reduceNum) {
for (Worker w : workers) {
w.shutdown();
count++;
}
}
}
}
/**
* 工作線程
*/
class Worker extends Thread {
private volatile boolean flag = true;
@Override
public void run() {
while (flag) {
Job job = null;
// 加鎖(若只有一個woker可不必加鎖,那就是所謂的單線程的線程池,線程安全)
synchronized (tasks) {
// 任務隊列爲空
while (tasks.isEmpty()) {
try {
// 阻塞,放棄對象鎖,等待被notify喚醒
tasks.wait();
System.out.println("block when tasks is empty");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 不爲空取出任務
job = tasks.removeFirst();
System.out.println("get job:" + job + ",do biz");
job.run();
}
}
}
public void shutdown() {
flag = false;
}
}
}
1 當調用wait()方法時線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備
2 Object的方法:void notify(): 喚醒一個正在等待該對象的線程。void notifyAll(): 喚醒所有正在等待該對象的線程。notifyAll使所有原來在該對象上等待被notify的線程統統退出wait狀態,變成等待該對象上的鎖,一旦該對象被解鎖,它們會去競爭。notify只是選擇一個wait狀態線程進行通知,並使它獲得該對象上的鎖,但不驚動其它同樣在等待被該對象notify的線程們,當第一個線程運行完畢以後釋放對象上的鎖,此時如果該對象沒有再次使用notify語句,即便該對象已經空閒,其他wait狀態等待的線程由於沒有得到該對象的通知,繼續處在wait狀態,直到這個對象發出一個notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖。
每調用一次就會創建一個擁有10個線程工作者的線程池。
public class TestService1 {
public static void main(String[] args) {
// 啓動10個線程
XYThreadPool<Runnable> pool = new XYThreadPool<Runnable>(10);
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("====1 test====");
}
});
}
}
public class TestService2 {
public static void main(String[] args) {
// 啓動10個線程
XYThreadPool<Runnable> pool = new XYThreadPool<Runnable>(10);
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("====2 test====");
}
});
}
}
希望在項目中所有的線程調用,都共用1個固定工作者數大小的線程池。
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import com.xy.pool.XYThreadPool;
/**
* 統一線程池管理類
*/
@Component
public class XYThreadManager {
private XYThreadPool<Runnable> executorPool;
@PostConstruct
public void init() {
executorPool = new XYThreadPool<Runnable>(10);
}
public XYThreadPool<Runnable> getExecutorPool() {
return executorPool;
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("testService3")
public class TestService3 {
@Autowired
private XYThreadManager threadManager;
public void test() {
threadManager.getExecutorPool().execute(new Runnable() {
@Override
public void run() {
System.out.println("====3 test====");
}
});
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("testService4")
public class TestService4 {
@Autowired
private XYThreadManager threadManager;
public void test() {
threadManager.getExecutorPool().execute(new Runnable() {
@Override
public void run() {
System.out.println("====4 test====");
}
});
}
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext atc = new ClassPathXmlApplicationContext("applicationContext.xml");
TestService3 t3 = (TestService3) atc.getBean("testService3");
t3.test();
TestService4 t4 = (TestService4) atc.getBean("testService4");
t4.test();
}
}