为什么会需要线程池技术?
- (1)Thread是一个重量级的资源,它的创建,启动以及销毁都是比较耗费性能的;重复利用线程,减少线程创建,销毁的开销,是一种好的程序设计习惯。
- (2)通过new Thread的方法创建线程难以管理,并且难以控制数量,线程的数量通常和系统的性能呈抛物线关系,合理控制线程数量才能发挥出系统最强的性能。
- (3)使用new Thread的方式创建的线程不利于扩展,比如定时,定期执行任务实现起来相对麻烦,但线程池提供了相应的稳定并且高效的解决方案。
线程池原理
所谓线程池,我们可以简单地把它理解成一个池子,只不过里面装的都是已经创建好的线程,当我们向这个池子提交任务时,池子中的某个线程就会主动地执行这个任务。当我们提交的任务数量大于池子中的线程时,线程池会自动创建线程加入池子中,但是会有数量的控制,就像游泳池里的水一样,当水达到一定的量时,就会溢出了。当任务比较少的时候,线程池会在系统空闲的时候自动地回收资源。为了能够异步地提交任务和缓存未处理的任务,需要有一个任务缓存队列。
线程池的组成要素
- 任务列队: 用来缓存提交的任务
- 任务拒绝策略: 如果线程数量已达上限并且任务队列也满了,就需要相应的拒绝策略来告诉提交任务的人。
- 线程工厂: 用于创建线程的,按需定制线程,执行我们的提交的任务。
- 线程管理功能: 一个线程池要想很好地管理和控制线程数量,可以通过后面的三个参数实现,
- 创建线程池时初始的线程数量init
- 线程池自动扩充线程时最大的数量:max。
- 线程池在空闲时需要释放线程但也需要维护一定数量的活跃数量或者核心数量core,
- 三者关系: init <= core <=max
- Keepedalive时间: 决定线程各个重要参数自动维护的时间间隔。
- QueueSize : 任务队列主要存放提交的Runnable ,为了防止内存溢出,有数量限制。
自定义线程池实现
话不多说,直接上源码,具体说明,请看注释。
当然这个线程池还存在不足,不足之处,请看结尾说明:
package com.zl.step8;
public interface ThreadPool {
// 提交任务到线程池 ,用于接受提交Runnable任务。
void execute(Runnable runnable) ;
// 关闭线程池
void shutdown();
// 获取线程的初始大小
int getInitSize();
// 获取线程池的最大线程数
int getMaxSize();
// 获取线程池的核心线程数量
int getCoreSize();
// 获取线程池中用于缓存的任务队列大小
int getQueueSize();
// 获取线程池中活跃线程的数量
int getActiveCount();
// 查看线程池是否已经被shutdown
boolean isShutdown();
}
package com.zl.step8;
/**
* 创建线程工厂
*
*/
@FunctionalInterface
public interface ThreadFactory {
// 创建线程
Thread createThread(Runnable runnable);
}
package com.zl.step8;
/**
* 任务队列,用于缓存提交的线程池的任务
*/
public interface RunnableQueue {
// 当有新的任务进来时 首先会offer到队列中
void offer(Runnable runnable);
// 获取Runnable
Runnable take() throws InterruptedException;
// 获取任务队列中任务的数量
int size() ;
}
package com.zl.step8;
/**
* 定义拒绝策略
*/
@FunctionalInterface
public interface DenyPoliy {
void reject(Runnable runnable , ThreadPool threadPool);
// 直接将任务丢弃
class DiscardDenyPolicy implements DenyPoliy{
@Override
public void reject(Runnable runnable, ThreadPool threadPool) {
//do nothing
}
}
// 直接报异常
class AbortDenyPolicy implements DenyPoliy {
@Override
public void reject(Runnable runnable, ThreadPool threadPool) {
throw new RunnableDenyExcetion("The runnable "+ runnable +" will be abort");
}
}
// 使任务咋提交者所在的线程中执行任务 不会加入到线程池中
class RunnerDenyPolicy implements DenyPoliy {
@Override
public void reject(Runnable runnable, ThreadPool threadPool) {
if(!threadPool.isShutdown()){
runnable.run();
}
}
}
}
package com.zl.step8;
import java.util.concurrent.atomic.AtomicInteger;
public class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger GROUP_COUNTER = new AtomicInteger(1) ;
private static final ThreadGroup group = new ThreadGroup("MyThreadPool-"+GROUP_COUNTER.getAndDecrement() ) ;
private static final AtomicInteger COUNTER = new AtomicInteger(0) ;
@Override
public Thread createThread(Runnable runnable) {
return new Thread(group,runnable,"thread-pool-"+COUNTER.getAndDecrement());
}
}
package com.zl.step8;
/**
* InternalTask是runnable的一个实现 ,主要用于线程内部,该咸亨会用到RunnableQueue
* 不断地从queue中取出某个runnable,并运行runnable中的run方法
*
*
*/
public class InternalTask implements Runnable {
private final RunnableQueue runnableQueue ;
private volatile boolean running = true ;
public InternalTask(RunnableQueue runnableQueue){
this.runnableQueue = runnableQueue ;
}
// 如果当前任务为running状态,并且没有中断 ,
// 则会不断的从队列中获取runnable,执行run方法。
// 一个线程Thread 可以执行Runnable中的Runnable中的run方法
@Override
public void run() {
while (running && ! Thread.currentThread().isInterrupted()) {
try {
Runnable task = runnableQueue.take() ;
task.run();
}catch (Exception e) {
running = false ;
break;
}
}
}
// 停止当前任务
public void stop(){
this.running = false ;
}
}
package com.zl.step8;
import java.util.LinkedList;
public class LinkedRunnableQueue implements RunnableQueue {
// 任务队列的最大容量,在构造时传入
private final int limit ;
// 拒绝策略,当队列满的时候执行
private final DenyPoliy denyPoliy ;
// 任务队列
private final LinkedList<Runnable> runnableList = new LinkedList<>() ;
// 线程池
private final ThreadPool threadPool ;
public LinkedRunnableQueue(int limit ,DenyPoliy denyPoliy , ThreadPool threadPool ){
this.limit = limit ;
this.denyPoliy = denyPoliy ;
this.threadPool = threadPool ;
}
// 存放任务至队列
@Override
public void offer(Runnable runnable) {
synchronized (runnableList){
if(runnableList.size() >= limit){
// 无法容纳新的任务时,执行拒绝策略
denyPoliy.reject(runnable,threadPool);
}else{
// 将任务加入队列,并唤醒
runnableList.addLast(runnable);
runnableList.notifyAll();
}
}
}
@Override
public Runnable take() throws InterruptedException {
synchronized (runnableList){
while (runnableList.isEmpty()){
try {
// 如果没有可执行的任务,则挂起,等待唤醒 继续执行
runnableList.wait();
}catch (InterruptedException e) {
throw e ;
}
}
// 从任务队列头部移除一个任务并将该任务返回
return runnableList.removeFirst();
}
}
@Override
public int size() {
synchronized (runnableList){
// 返回当前任务队列的任务数
return runnableList.size();
}
}
}
package com.zl.step8;
public class RunnableDenyExcetion extends RuntimeException {
public RunnableDenyExcetion(String message) {
super(message);
}
}
package com.zl.step8;
public class ThreadTask {
Thread thread;
InternalTask internalTask;
public ThreadTask(Thread thread , InternalTask internalTask){
this.internalTask = internalTask ;
this.thread = thread ;
}
}
package com.zl.step8;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
public class BasicThreadPool extends Thread implements ThreadPool {
// 初始化线程数量
private final int initSize;
// 最大线程数量
private final int maxSize ;
// 核心线程数量 ,当线程平稳的时候的核心数量啊
private final int coreSize ;
// 活跃线程数量
private int activeCount ;
// 创建线程所需要的线程工厂
private final ThreadFactory threadFactory ;
// 任务队列
private final RunnableQueue runnableQueue ;
// 是否关闭
private volatile boolean isShutdown = false ;
// 工作线程队列
private final Queue<ThreadTask> threadQueue = new ArrayDeque<>() ;
// 默认线程工厂
private final static ThreadFactory DEFAULT_THREAD_FACTORY = new DefaultThreadFactory();
// 默认拒绝策略
private final static DenyPoliy DEFAULT_DENY_POLICY = new DenyPoliy.DiscardDenyPolicy();
// 线程池的检查更新线程的时间间隔
private final long keepAliveTime ;
// 时间处理对象
private final TimeUnit timeUnit ;
public BasicThreadPool(int initSize, int maxSize , int coreSize , int queueSize) {
this(initSize,maxSize,coreSize,DEFAULT_THREAD_FACTORY,
queueSize,DEFAULT_DENY_POLICY,10,TimeUnit.SECONDS) ;
}
public BasicThreadPool(int initSize,
int maxSize ,
int coreSize ,
ThreadFactory threadFactory,
int queueSize,
DenyPoliy denyPoliy,
long keepAliveTime ,
TimeUnit timeUnit
) {
this.initSize = initSize ;
this.maxSize = maxSize ;
this.coreSize = coreSize ;
this.threadFactory = threadFactory ;
this.runnableQueue = new LinkedRunnableQueue(queueSize, denyPoliy, this) ;
this.keepAliveTime = keepAliveTime ;
this.timeUnit = timeUnit ;
this.init();
}
// 初始化操作,启动线程
private void init(){
start();
for (int i = 0; i<initSize ;i ++) {
newThread();
}
}
// 创建线程
private void newThread() {
InternalTask internalTask = new InternalTask(runnableQueue) ;
Thread thread = this.threadFactory.createThread(internalTask) ;
ThreadTask threadTask = new ThreadTask(thread,internalTask);
threadQueue.offer(threadTask);
this.activeCount++ ;
thread.start();
}
// 移除线程
private void removeThread(){
ThreadTask threadTask = threadQueue.remove() ;
threadTask.internalTask.stop();
this.activeCount-- ;
}
// 执行任务 其实就是将任务放到队列中
@Override
public void execute(Runnable runnable) {
if(this.isShutdown){
throw new IllegalStateException("The thread poll is destory ") ;
}else{
this.runnableQueue.offer(runnable);
}
}
@Override
public void run(){
while (!isShutdown && !isInterrupted() ) {
// 定期检查
try {
timeUnit.sleep(keepAliveTime);
} catch (InterruptedException e) {
isShutdown = true ;
break;
}
synchronized (this) {
if(isShutdown){
break;
}
// 当前的队列中有任务尚未处理,并且活跃线程 小于 核心线程 继续扩容
if(runnableQueue.size() > 0 && activeCount < coreSize ){
for (int i = initSize ; i < coreSize ; i++) {
newThread();
}
// continue的目的在于不想让线程一次性达到最大值 maxSize 。 缓慢增长
continue;
}
// 当前的队列中有任务尚未处理,并且活跃线程 小于 最大线程 继续扩容
if(runnableQueue.size() > 0 && activeCount < maxSize ){
for (int i = coreSize ; i<maxSize ; i++ ) {
newThread();
}
}
// 当前的队列中没有任务需要处理,并且活跃线程 大于 核心线程 则移除多余的线程
if(runnableQueue.size() ==0 && activeCount > coreSize ){
for (int i = coreSize ; i< activeCount ; i++) {
removeThread();
}
}
}
}
}
// 线程销毁
@Override
public void shutdown() {
synchronized (this){
if(isShutdown) return;
isShutdown = true ;
threadQueue.forEach(threadTask -> {
threadTask.internalTask.stop();
threadTask.thread.interrupt();
});
this.interrupt();
}
}
@Override
public int getInitSize() {
if(isShutdown){
throw new IllegalStateException(" The thread pool is destory . ") ;
}
return this.initSize;
}
@Override
public int getMaxSize() {
if(isShutdown){
throw new IllegalStateException(" The thread pool is destory . ") ;
}
return this.maxSize;
}
@Override
public int getCoreSize() {
if(isShutdown){
throw new IllegalStateException(" The thread pool is destory . ") ;
}
return this.coreSize;
}
@Override
public int getQueueSize() {
if(isShutdown){
throw new IllegalStateException(" The thread pool is destory . ") ;
}
return runnableQueue.size();
}
@Override
public int getActiveCount() {
// 获取活跃线程数,需要进行同步操作
synchronized (this) {
return this.activeCount;
}
}
@Override
public boolean isShutdown() {
return this.isShutdown;
}
}
测试运行:
package com.zl.step8;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
// 定义线程池,初始化线程数2, 核心线程4, 最大线程6, 任务队列最大值1000
final ThreadPool threadPool = new BasicThreadPool(2,6,4,1000) ;
// 定义20个任务提交给线程池
for (int i= 0 ; i< 20 ; i++) {
threadPool.execute(()->{
try {
TimeUnit.SECONDS.sleep(10);
System.out.println(Thread.currentThread().getName()+" is running and done .");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 不断输出信息,用于查看线程池的状况
// for (;;){
// System.out.println("活跃线程数 ActiveCount:"+threadPool.getActiveCount());
// System.out.println("线程队列数量 QueueSize:"+threadPool.getQueueSize());
// System.out.println("核心线程数 CoreSize:"+threadPool.getCoreSize());
// System.out.println("线程最大值 MaxSize:"+threadPool.getMaxSize());
// System.out.println("=========================================");
// TimeUnit.SECONDS.sleep(5);
// }
TimeUnit.SECONDS.sleep(12);
threadPool.shutdown();
// Thread.currentThread().join();
}
}
不足:
1.BasicThreadPool和Thread不应该是继承关系,采用组合关系更为妥当,
这样就可以避免调用者直接使用BasicThreadPool中的Thread方法
2.线程池的销毁功能尚未返回未被处理的任务,这样未被处理的任务将被丢弃
3.BasicThreadPool构造函数过多,采用Builder和设计模式对齐进行封装或者提供工厂方法进行构造
4.线程池中的数量控制没有进行合法性校验,比如initSize数量不应该大于maxSize数量
5.其他缺点以及优化请自行参考。
本文来源于:
《Java高并发编程详解:多线程与架构设计》 --汪文君