假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。当T1 + T3 远大于 T2时,采用多线程技术可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。

线程池就是一个线程的容器,每次只执行额定数量的线程, 线程池作用就是限制系统中执行线程的数量。采用线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目。

为什么要用线程池:

1)减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务

2)可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)

Java1.5之后,Java 提供了自己的线程池ThreadPoolExecutor类。

ThreadPoolExecutor使用简介

线程池类为java.util.concurrent.ThreadPoolExecutor,常用构造方法为:

public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
}

corePoolSize 指的是保留的线程池大小。

maximumPoolSize 指的是线程池的最大大小。

keepAliveTime 指的是空闲线程结束的超时时间。

unit 是一个枚举,表示 keepAliveTime 的单位。

workQueue 表示存放任务的队列。

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

线程池的工作过程如下:

1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

2、当调用 execute() 方法添加一个任务时,线程池会做如下判断:

a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

b. 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列。

c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;

d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。

3、当一个线程完成任务时,它会从队列中取下一个任务来执行。

4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

这样的过程说明,并不是先加入任务就一定会先执行。假设队列大小为10,corePoolSize为3,maximumPoolSize 为6,那么当加入 20 个任务时,执行的顺序就是这样的:首先执行任务 1、2、3,然后任务4~13被放入队列。这时候队列满了,任务 14、15、16 会被马上执行,而任务 17~20 则会抛出异常。最终顺序是:1、2、3、14、15、16、4、5、6、7、8、9、10、11、12、13。

下面是一个线程池使用的例子:

public class TestThreadPool {    

    public static void main(String[] args) {    

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 6, 5,TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());    

         for (int i = 1; i <= 20; i++) {     

             threadPool.execute(new ThreadPoolTask());    

             //threadPool.shutdown();
}
}
} 创建 ThreadPoolTask类: public class ThreadPoolTask implements Runnable {
public void run() {
try {
System.out.println("开始执行任务:" + attachData);
Thread.sleep(100);
}
catch(Exception e){
e.printStackTrace();
}
}
}

1、BlockingQueue 只是一个接口,常用的实现类有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好处在于没有大小限制。这样的话,因为队列不会满,所以 execute() 不会抛出异常,而线程池中运行的线程数也永远不会超过 corePoolSize 个,keepAliveTime 参数也就没有意义了。

2、shutdown() 方法不会阻塞。调用 shutdown() 方法之后,主线程就马上结束了,而线程池会继续运行直到所有任务执行完才会停止。如果不调用 shutdown() 方法,那么线程池会一直保持下去,以便随时添加新的任务。

最新文章

  1. echarts-在现实标题中显示百分比
  2. R语言Data Frame数据框常用操作
  3. ubifs扩展性分析
  4. 在学Go语言
  5. 为Python添加默认模块搜索路径
  6. perl 语法速查
  7. GS线程
  8. java 读取文件到String(解决中文乱码)
  9. ajax详解,以及异步JSOP的实现
  10. #添加屏蔽IP LINUX
  11. python 性能鸡汤
  12. VB.Net常用数学函数整理
  13. asp.net个人笔记
  14. Jekyll学习:基本使用方法
  15. 在Activity中为什么要用managedQuery()
  16. hdu_5800_To My Girlfriend(变种背包)
  17. Linux用户登录日志查询
  18. iOS----------常用三方库
  19. Vue中 $ref 的用法
  20. NLog类库使用探索——编程配置

热门文章

  1. mysql root用户密码个性
  2. ORID方法在敏捷中的利用
  3. GDKOI2016 爆零记
  4. linux命令(6)crontab的用法和解析,修改编辑器
  5. CentOS下安装网卡驱动
  6. jquery ajax api
  7. asp.net添加用户
  8. OpenERP 7 picking order 继承需要注意的地方
  9. C#中利用JQuery实现视频网站的缩略图采集
  10. elasticsearch安装与使用(2)-- centos7 安装测试的集群工具elasticsearch head