Java自定義線程池和線程總數控制

1 概述

池化是常見的思想,線程池是非常典型的池化的實現,《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,而不是鎖


3 無需控制線程總數
每調用一次就會創建一個擁有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====");
			}
		});
	}
}


4 控制線程總數
希望在項目中所有的線程調用,都共用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();
	}

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章