写在前面的话:之前一直没了解过多线程编程相关的东西,对于线程之类相关的概念也是一知半解。这次借着准备面试的时机,计划将一些关键性的盲区(除多线程以外,还有垃圾回收、图片缓存、性能优化等等)都彻底扫一遍。接下来我会陆续整理成学习笔记,主要作为自己学习后的温习巩固之用。如果能帮助到同样迷茫的小白的话,深感荣幸;如果因为缺少实战导致理解有误或解释不清的地方,还请路过的大牛不吝赐教,共同进步^_^
知识点相关
作为这次学习的切入点,线程池关联到的概念有很多,比如线程到底是什么,常搭配使用的Runnable又是啥,为什么又要搭配起来使用,等等。
这是一张继承关系图。最初的Executor接口定义的是execute方法,比较简单。它的子类ExecutorService接口,你可以理解为是通用线程池类,里面增加了线程池常用的一些用于控制线程的方法,如submit和shutdown等,用于管理生命周期。继承的AbstractExecutorService抽象类,是在ExecutorService基础上的默认实现。最后,ThreadPoolExecutor继承自该抽象类,通过构造方法来配置线程池。
这里还要提一下Executors类,不在图中,是线程池工厂类,可以通过一系列静态方法创建线程池。查看源码可以发现,方法内部都是使用ThreadPoolExecutor的构造方法加上不同的配置参数来实现。
下面是ThreadPoolExecutor类的几个构造方法:

重点讲第三个,其他的构造方法基本类似。其中的参数有(按顺序依次):核心线程数N,最大线程数M,超时时间T,时间单位U,缓冲队列B和拒绝处理Handler。这些参数各自代表的含义,将结合不同的缓冲队列类型分情况讲解:
- SynchronousQueue类型:所有任务不进入缓冲队列(可以理解为缓冲队列的容量为0),直接进入可用线程执行,当无可用线程时则直接创建新线程执行任务。当线程数超过M时,调用Handler处理被拒绝的新任务。
- LinkedBlockingQueue类型:有新任务时,若当前线程数少于N,则创建新线程;若大于等于N,则进入缓冲队列;当缓冲队列充满时,且线程数少于M,则继续创建新线程,直至达到M时调用Handler处理拒绝任务。一般情况下,LinkedBlockingQueue作为无界队列不指定缓冲容量,可以无限扩展,此时相当于M失效;但也可以通过构造函数来指定容量。
- ArrayBlockingQueue类型:规则与LinkedBlockingQueue类似,但一般情况下指定队列长度。
1 package com.effective.java.concurrent.task; 2 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.ExecutionException; 5 import java.util.concurrent.ExecutorService; 6 import java.util.concurrent.Executors; 7 import java.util.concurrent.Future; 8 import java.util.concurrent.FutureTask; 9 10 public class RunnableFutureTask { 11 12 static ExecutorService mExecutor = new ThreadPoolExecutor(2, 3, 5, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); 13 14 15 public static void main(String[] args) { 16 futureDemo(); 17 } 18 19 20 static void futureDemo() { 21 try { 22 /** 23 * 提交runnable则没有返回值, future没有数据 24 */ 25 Future<?> result = mExecutor.submit(new
