为了更好的使用多线程,JDK提供了线程池供开发人员使用,目的在于减小线程的建立和销毁次数,以此达到线程的重复利用。java
其中ThreadPoolExecutor是线程池中最核心的一个类,咱们先简单看一下这个类的继承关系。程序员
其中Executor是线程池的顶级接口,接口中只定义了一个方法 void execute(Runnable command);线程池的操做方法都是定义子在ExecutorService子接口中的,因此说ExecutorService是线程池真正的接口。多线程
ThreadPoolExecutor提供了四个构造方法,咱们看一下参数最全的一个构造函数;ide
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
}ThreadFactory threadFactory,
函数的参数含义以下:函数
本节咱们主要对前五个参数中的corePoolSize,maximumPoolSize及workQueue是如何配合使用作出说明(keepAliveTime,unit主要对空闲线程的存活时间作的定义,见名知意,再也不作出说明),以此来引出线程池的一些特性。spa
threadFactory和handler这两个参数都有默认值,对于它们的用法将放到其它章节去作说明。线程
特性一:当池中正在运行的线程数(包括空闲线程)小于corePoolSize时,新建线程执行任务。code
下面用实验来讲明,代码以下:blog
public class TestThreadPoolExecutor { public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1)); //任务1 pool.execute(new Runnable() { @Override public void run() { System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName()); } }); try { //主线程睡2秒 Thread.sleep(2*1000); } catch (InterruptedException e) { e.printStackTrace(); } //任务2 pool.execute(new Runnable() { @Override public void run() { System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName()); } }); } }
实验结果以下:
继承
实验结果分析:
从实验结果上能够看出,当执行任务1的线程(thread-1)执行完成以后,任务2并无去复用thread-1而是新建线程(thread-2)去执行任务。
特性二:当池中正在运行的线程数大于等于corePoolSize时,新插入的任务进入workQueue排队(若是workQueue长度容许),等待空闲线程来执行。
下面用实验来讲明,代码以下:
public class TestThreadPoolExecutor { public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1)); // 任务1 pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(3 * 1000); System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 任务2 pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(5 * 1000); System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 任务3 pool.execute(new Runnable() { @Override public void run() { System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName()); } }); } }
实验结果以下:
实验结果分析:
从实验结果上看,任务3会等待任务1执行完以后,有了空闲线程,才会执行。并无新建线程执行任务3,这时maximumPoolSize=3这个参数不起做用。
特性三:当队列里的任务数达到上限,而且池中正在运行的线程数小于maximumPoolSize,对于新加入的任务,新建线程。
下面用实验来讲明,代码以下:
public class TestThreadPoolExecutor { public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1)); // 任务1 pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(3 * 1000); System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 任务2 pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(5 * 1000); System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 任务3 pool.execute(new Runnable() { @Override public void run() { System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName()); } }); // 任务4 pool.execute(new Runnable() { @Override public void run() { System.out.println("-------------helloworld_004---------------" + Thread.currentThread().getName()); } }); } }
实验结果以下:
实验结果分析:
当任务4进入队列时发现队列的长度已经到了上限,因此没法进入队列排队,而此时正在运行的线程数(2)小于maximumPoolSize因此新建线程执行该任务。
特性四:当队列里的任务数达到上限,而且池中正在运行的线程数等于maximumPoolSize,对于新加入的任务,执行拒绝策略(线程池默认的拒绝策略是抛异常)。
下面用实验来讲明,代码以下:
public class TestThreadPoolExecutor { public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1)); // 任务1 pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(3 * 1000); System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 任务2 pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(5 * 1000); System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 任务3 pool.execute(new Runnable() { @Override public void run() { System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName()); } }); // 任务4 pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("-------------helloworld_004---------------" + Thread.currentThread().getName()); } }); // 任务5 pool.execute(new Runnable() { @Override public void run() { System.out.println("-------------helloworld_005---------------" + Thread.currentThread().getName()); } }); } }
实验结果以下:
实验结果分析:
当任务5加入时,队列达到上限,池内运行的线程数达到最大,故执行默认的拒绝策略,抛异常。
本文中使用到的队列类型虽然仅限于LinkedBlockingQueue这一种队列类型,但总结出来的特性,对与经常使用ArrayBlockingQueue 和 SynchronousQueue一样适用,些许不一样及三种队列的区别,将在下个章节中说明。
最后说一点,咱们做为程序员,研究问题仍是要仔细深刻一点的。当你对原理了解的有够透彻,开发起来也就驾轻就熟了,不少开发中的问题和疑惑也就迎刃而解了,并且在面对其余问题的时候也可作到举一反三。固然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,你有大把的时间或者空余的时间,你大可去刨根问底,深刻的去研究一项技术,为以为这对一名程序员的成长是很重要的事情。