java线程池之ThreadPoolExecutor(一)

一 引言:

        在前面的几篇博客中,我们知道,当需要使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?在Java中可以通过线程池来达到这样的效果。今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPoolExecutor类开始讲起。

    ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程调度,线程池管理,监控等等服务。


二  线程池的创建及参数说明

    这里以一个最常用的构造方法来说明线程池的创建.

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                                  new ArrayBlockingQueue<Runnable>(5));
        第一个参数5===》corePoolSize:核心线程池大小。可以理解为就是线程池的大小。有人会联想起数据库里面的连接池,认为这是初始连接大小,其实两者是有区别的。连接池的初始连接大小是在连接池启动时就会建立好多个(比方说这里的5个)数据库连接对象的。但是在线程池这里是不会初始建立5个线程对象的,而是要等具体的任务来了才会逐个建立线程。当任务执行完毕,线程池中的线程就会逐个中止并释放资源,但是仍然会保持5个空闲,继续等待接受新的任务。就是这个意思。
当然,在实际中如果需要线程池创建之后立即创建线程,可以通过以下两个方法办到:
prestartCoreThread():初始化一个核心线程;
prestartAllCoreThreads():初始化所有核心线程

      第二个参数10===》maximumPoolSize:池中允许的最大线程数。这里表示线程池中最多同时只能允许10个任务在执行。

      第三个参数200===》keepAliveTime :当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。怎么理解呢?

打个比方:工厂里有正式员工5人(corePoolSize),突然来了10个任务,那么就要临时招5个人过来帮忙,任务做完了,老板并不想立刻辞退他们,而是花钱继续养着以防止又有大量的任务到来。但是如果一直没有多余的任务来,就不能一直养着,所以一段时间后就辞退他们。这里的一段时间就是这里的第三个参数200,也就是说线程空闲了200个单位时间就会被中止。那么这200个单位时间是秒,毫秒,还是分钟,小时呢,请看第四个参数。

      第四个参数TimeUnit.MILLISECONDS===》unit:keepAliveTime 参数的时间单位。这里表示的是毫秒。

      第五个参数new ArrayBlockingQueue<Runnable>(5)===》workQueue : 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。这个参数最不好理解。假如现在线程池里面有5个任务正在运行,这时候来了第6个任务,就会到此队列里面等待,来了第七个第八个同理排队等待,直到来了第11个任务,队列已满,但是线程池里面5个核心线程仍然没有停止,那么这时候就会创建新的线程来运行队列里面排队的任务,这时候正在运行的线程就是6个。当来了第15个任务之后,假如前面的任务还没有完成,那么这时候线程池里面一共有10个任务在跑了,已经达到了第二个参数maximumPoolSize:最大活跃线程数的限制,这时候线程池就不再接收新的任务了,就必须等待池子里面有空闲线程为止。

     举个例子,有个公司面试,同时有5个面试官可以面试5个人。房间里面还有5个可以供预备面试的人排队等候的座位。这时候来了8个人,那么有5个人正在面试,3个人正在等待。然后面试者增加到12人,前面5个人还在面试,那么这时剩下的7人里面只有5个人可以坐在房间里面排队休息,还有2人只能站在门外。公司发现面试的人比较多,让人站在外面不近人情,然后又临时抽调2个面试官过来面试,这时候刚好7个在面试,5个在等待,没有站在门外的。好,然后又来人了,公司又临时抽调面试官过来。一直到房间里面有10个面试官正在面试,公司已经无法继续临时抽调面试官过来,那么再来的面试者只能被拒之门外了。就是这个道理,明白了没!

三 案例

/*
 * 线程检测类
 */
class CheckTask implements Runnable {
	ThreadPoolExecutor executor;
	public CheckTask(ThreadPoolExecutor executor) {
		this.executor=executor;
	}
	/**
	 * 线程检测
	 */
	public void showPoolInfo(ThreadPoolExecutor executor,int i){
		System.out.println("=========第"+i+"次检测start===========");
		System.out.println("线程池中核心线程数:"+executor.getCorePoolSize());
		System.out.println("线程池中活跃线程数目:"+executor.getActiveCount());
		System.out.println("线程池中允许的最大线程数目:"+executor.getMaximumPoolSize());
		System.out.println("队列中等待执行的任务数目:"+executor.getQueue().size());
		System.out.println("已经执行完成的任务数目:"+executor.getCompletedTaskCount());
		System.out.println("=========第"+i+"次检测end===========");
	}
	public void run() {
		int cnt=0;
		 while(true){
			cnt++;
			try {
				Thread.sleep(1000);//每过1s检测一次
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			showPoolInfo(executor,cnt);
			if(executor.isTerminated()){
				break;
			}
		 }
	}
}
=========================================

/*
 * 任务类
 */
class MyTask implements Runnable {
    String theTask;
    public MyTask(String theTask) {
        this.theTask = theTask;
    }
     
    public void run() {
        System.out.println("正在执行"+theTask);
        try {
        	//需要一定时间秒执行完毕
            Thread.currentThread().sleep((long)(Math.random()*10000));
        } catch (InterruptedException e) {
        	System.out.println("线程:"+theTask+"被中断");
        }
        System.out.println("线程:"+theTask+"执行完毕");
    }
}
=======================================

/*
 * 主线程类
 */
public class ThreadPoolTest {
	
	public static void main(String[] args) {
		
		//创建线程池对象
		ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                                  new ArrayBlockingQueue<Runnable>(5));
		
                 //该线程用于对线程池的检测
		 new Thread(new CheckTask(executor)).start();

                //预先创建核心线程
		executor.prestartAllCoreThreads();
		for(int i=1;i<=15;i++){//执行15个任务,但最多只有10个线程同时运行。
                     MyTask myTask = new MyTask("task=="+i);
                     executor.execute(myTask);
                }
		 
		 executor.shutdown();
	}
}

=================================运行结果================

正在执行task==2
正在执行task==4
正在执行task==11
正在执行task==1
正在执行task==13
正在执行task==15
正在执行task==5
正在执行task==3
正在执行task==12
正在执行task==14
=========第1次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:5
已经执行完成的任务数目:0
=========第1次检测end===========
线程:task==3执行完毕
正在执行task==6
线程:task==14执行完毕
正在执行task==7
=========第2次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:3
已经执行完成的任务数目:2
=========第2次检测end===========
线程:task==7执行完毕
正在执行task==8
=========第3次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:2
已经执行完成的任务数目:3
=========第3次检测end===========
线程:task==15执行完毕
正在执行task==9
线程:task==6执行完毕
正在执行task==10
=========第4次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:5
=========第4次检测end===========
线程:task==8执行完毕
线程:task==12执行完毕
线程:task==9执行完毕
=========第5次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:7
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:8
=========第5次检测end===========
线程:task==2执行完毕
线程:task==11执行完毕
=========第6次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:10
=========第6次检测end===========
线程:task==1执行完毕
=========第7次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:4
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:11
=========第7次检测end===========
=========第8次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:4
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:11
=========第8次检测end===========
线程:task==5执行完毕
=========第9次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:3
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:12
=========第9次检测end===========
线程:task==4执行完毕
线程:task==13执行完毕
=========第10次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:1
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:14
=========第10次检测end===========
线程:task==10执行完毕
=========第11次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:0
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:15
=========第11次检测end===========


============================

如果我们这时候把等候队列的5改成20,即new ArrayBlockingQueue<Runnable>(5)  ====》new ArrayBlockingQueue<Runnable>(20)  即等候队列足够大

那么运行效果如下

正在执行task==1
正在执行task==2
正在执行task==3
正在执行task==4
正在执行task==5
=========第1次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:10
已经执行完成的任务数目:0
=========第1次检测end===========
线程:task==4执行完毕
正在执行task==6
=========第2次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:9
已经执行完成的任务数目:1
=========第2次检测end===========
线程:task==3执行完毕
正在执行task==7
=========第3次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:8
已经执行完成的任务数目:2
=========第3次检测end===========
线程:task==1执行完毕
正在执行task==8
线程:task==8执行完毕
正在执行task==9
=========第4次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:6
已经执行完成的任务数目:4
=========第4次检测end===========
线程:task==7执行完毕
正在执行task==10
线程:task==2执行完毕
正在执行task==11

......
=========第13次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:2
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:13
=========第13次检测end===========
=========第14次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:2
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:13
=========第14次检测end===========
线程:task==12执行完毕
=========第15次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:1
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:14
=========第15次检测end===========
=========第16次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:1
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:14
=========第16次检测end===========
=========第17次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:1
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:14
=========第17次检测end===========
=========第18次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:1
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:14
=========第18次检测end===========
线程:task==15执行完毕
=========第19次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:0
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:15
=========第19次检测end===========
=====================================

这时候我们发现线程里面的活跃线程数始终不超过5个,因为队列的容量足够大,没有执行的任务都排队等候即可
关于线程入队列和任务丢弃原则请查看续篇。
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御前两把刀刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值