Java的线程池实现是ThreadPoolExecutor类
同时封装了一个工具类Executors来获取不同类型的线程池
Executors类的构造方法其实就是调用了ThreadPoolExecutor类的构造方法来创建线程池的

Executors类下的四种创建线程池的方式

1 可缓存线程池

ExecutorService pool = Executors.newCachedThreadPool();
创建一个不限线程数上限的线程池
即当有新的任务提交时 若有空闲的线程则直接处理 若没有空闲的就创建新的线程处理任务 队列中是不存储任务的
线程池不对线程池大小做限制 线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小
如果线程空闲时间超过了60秒就会被回收
即没有核心线程数 都是临时工 没有正式员工
来了任务就干 干完等一会儿 要是还有活儿就继续干 要是等了一分钟还没有活 就不干了(销毁)

  1. //源码
  2. public static ExecutorService newCachedThreadPool() {
  3. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
  4. 60L, TimeUnit.SECONDS,
  5. new SynchronousQueue<Runnable>());
  6. }
  7. //使用
  8. pool.execute(new Runnable() {
  9. @Override
  10. public void run() {
  11. System.out.println("可缓存线程池");
  12. }
  13. });

2 定长线程池

ExecutorService pool = Executors.newFixedThreadPool(int nThreads);
它创建了一个固定大小的线程池 每次提交一个任务就创建一个线程 直到线程达到线程池的最大值nThreads
线程池的大小一旦达到最大值后 再有新的任务提交时则放入无界阻塞队列中 等到有线程空闲时 再从队列中取出任务继续执行
即和SingleThread线程池逻辑一模一样 只不过这个指定了线程数 队列无穷大

  1. //源码
  2. public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
  3. return new ThreadPoolExecutor(nThreads, nThreads,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>(),
  6. threadFactory);
  7. }
  8. //使用
  9. pool.execute(new Runnable() {
  10. @Override
  11. public void run() {
  12. System.out.println("定长线程池");
  13. }
  14. });

3 单线程线程池

ExecutorService pool = Executors.newSingleThreadExecutor();
它创建了一个单线程的线程池 且只会用唯一的工作线程来执行任务 保证所有任务按照指定顺序执行
等待队列无穷大 一个一个来

  1. //源码
  2. public static ExecutorService newSingleThreadExecutor() {
  3. return new FinalizableDelegatedExecutorService
  4. (new ThreadPoolExecutor(1, 1,
  5. 0L, TimeUnit.MILLISECONDS,
  6. new LinkedBlockingQueue<Runnable>()));
  7. }
  8. //使用
  9. pool.execute(new Runnable() {
  10. @Override
  11. public void run() {
  12. System.out.println("单线程线程池");
  13. }
  14. });

4 计划任务线程池

ScheduledExecutorService pool = Executors.newScheduledThreadPool(int);
这个方法创建了一个固定大小的线程池 方法参数即为线程池中的线程个数 且支持定时及周期性任务执行
当线程数达到核心线程数之后 就创建新的线程 可以创建无穷多个线程
新创建出来的线程 只要完成了就销毁

  1. pool.schedule(new Runnable() {
  2. @Override
  3. public void run() {
  4. System.out.println("ScheduledExecutorService");
  5. }
  6. }, 3, TimeUnit.SECONDS);
  7. //第二个参数表示 隔多长时间后再执行该方法
  8. //第三个参数表示 时间的单位
  9. //如上表示隔3秒后执行

自定义线程池

Java线程池本质只有一个 无论哪个线程池 都是调用ThreadPoolExecutor线程池创建出来的
为什么不同的线程池会有不同的特性呢?参数不一样
上述的四种线程池可能都无法满足某些需求 我们可以使用ThreadPoolExecutor这个类来进行自定义线程池

  1. ExecutorService myPool = new ThreadPoolExecutor(
  2. 5,
  3. 10,
  4. 60L,
  5. TimeUnit.SECONDS,
  6. new LinkedBlockingQueue(50),
  7. Executors.defaultThreadFactory(),
  8. new ThreadPoolExecutor.AbortPolicy()
  9. );
  10. // 第一个参数 corePoolSize 核心线程数(即一开始初始化多少个线程)
  11. // 第二个参数 maximumPoolSize 最大线程数量
  12. // 第三个参数 keepAliveTime 线程保持等待任务的最大空余时间/保持活跃的时间
  13. // 即线程空闲等待任务的时间若超过了最大空闲时间 那么线程池就会把多余的线程给销毁
  14. // 直到线程池中的线程数只剩下核心线程数所规定的个数
  15. // 第四个参数 unit 时间单位 和第三个参数一起使用
  16. // 第五个参数 workQueue 任务队列(若有新任务 但无空闲的线程时 会将任务放入指定的阻塞队列中)
  17. // 第六个参数 Executors.defaultThreadFactory() 创建线程的工厂(即线程池要创建线程时调用的方法)
  18. // 第七个参数 defaultHandler 拒绝策略(也就是队列满而且也达到了最大线程数的时候,同时还有新的任务,此时就会触发拒绝策略)
  19. // 1)丢弃任务抛出异常
  20. // 2)丢弃任务 不抛出异常
  21. // 3)丢弃队列最前面的任务
  22. // 4)由调用线程处理该任务
  23. 例子
  24. 银行平时就开三个窗口(核心线程数)今天来的人多 有人需要等待
  25. 等待的人就坐在大厅里 这个大厅就是(等待队列)
  26. 如果大厅坐满了 银行会觉得今天人太多了 我们得多开几个窗口 此时银行会开启更多窗口 但是不可能大于总得窗口数(最大线程数)
  27. 当银行开启了足够多得窗口之后 大厅中得人数明显开始减少了
  28. 当大厅中的人都办完业务离开 2分钟之后(保持活跃的时间,时间单位)后开的那些窗口 觉得自己任务完成了 就会将窗口关闭
  29. 当银行开启了足够多的窗口之后 大厅中的人数还在增加 并且装不下了
  30. 此时银行会告知后来的人 今天可能办不完业务了(拒绝策略) 可以明天再来
  31. 希望有最基本的线程数 任务多的时候 可以创建新的线程
  32. 但是还希望 多创建出来的线程 当任务比较堆积的时候 不要立即销毁 还能够继续执行任务 当真的完全没有任务的时候再销毁

注意
1)线程池在刚创建完成的时候 有几个线程?
线程池在刚创建出来的时候 里面一个线程都没有
2)什么时候才会有线程?
在我们向线程池提交任务的时候 线程池才会开始创建线程
3)如果核心线程数是10 最大线程数是20 当我们提交第11个任务的时候(前十个没有结束)线程池会创建新的线程吗?
不会的
线程池创建线程的策略:先把核心线程数填满 然后填满等待队列 等待队列如果满了 就创建新的线程 但是总线程数不能大于最大线程数