说线程池队列之前,还需要先知道如何构建线程池。JDK 已经为我们提供了创建线程池的工具类了,只需要我们调用即可。下面我们看看这个工具类提供的几种线程池:
-
newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 -
newFixedThreadPool
构建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 -
newScheduledThreadPool
构建一个定长线程池,支持定时及周期性任务执行。 -
newSingleThreadExecutor
构建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
再这里提前说一点注意事项
阿里巴巴 java 开发规约里却不允许直接使用这个类来构建线程池。
我们先看看该工具类其中一个构建线程池的方法
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//注意这个 Integer.MAX_VALUE
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
我们可以看到,第二个参数居然是 Integer.MAX_VALUE ,前面一片文章介绍 java 线程池的时候说过第二个参数是线程池中最大线程数,一般我们需要设定一个合适的值,但是这个默认使用的是 Integer.MAX_VALUE,可以理解是无限大小了,可以无限创建线程,根本不可控。可能某天就出现了莫名其妙的异常了。
上面说了那么多,下面我们来看每种线程池的使用:
首先我们来看 newCachedThreadPool 线程池:
上面已经展示了 newCachedThreadPool 源码,这里直接 Demo。
/**
* @Auther: Gentle
* @Date: 2019/3/22 13:05
* @Description:
*/
public class CreateThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//构建线程池,这里举可缓存的线程池,可以试试别的线程池。
ExecutorService executorService = Executors.newCachedThreadPool();
//构建一个没有回调的执行任务
MyTask gentle = new MyTask("Gentle");
for (int i=0;i<10;i++) {
executorService.execute(gentle);
}
//关闭线程池
executorService.shutdown();
}
//无返回值任务
static class MyTask implements Runnable{
private String name;
public MyTask(String name){
this.name=name;
}
@Override
public void run() {
System.out.println("线程名字:"+Thread.currentThread().getName() + " 传入名字:"+name);
}
}
}
运行结果如下:
由于线程池是会重用线程,所以看到图中有重用的线程。
newSingleThreadExecutor 线程池
先看看 newSingleThreadExecutor 单线程线程池的源代码,可以很清晰的看到,corePoolSize 和 maximumPoolSize 都为 1。这样的设计只允许线程池中存在一条线程,所有任务也只能靠这一条线程去执行,这就避免了多线程竞争问题。
先看看该线程池的源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
下面是使用 Demo:
/**
* @Auther: Gentle
* @Date: 2019/3/22 13:05
* @Description:
*/
public class CreateThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//单线程线程池池的的构建
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
//构建任务
MyTask gentle = new MyTask("Gentle");
//执行任务
for (int i=0;i<10;i++) {
executorService1.execute(gentle);
}
//关闭线程池
executorService1.shutdown();
}
//无返回值任务
static class MyTask implements Runnable{
private String name;
public MyTask(String name){
this.name=name;
}
@Override
public void run() {
System.out.println("线程名字:"+Thread.currentThread().getName() + " 传入名字:"+name);
}
}
}
下面的结果也可以清晰的看出,我们所有的任务都交由 1 个线程去完成。假设任务里要执行一段时间,那下一个任务则要等待上一个任务完成,才可以被执行。
定长的线程池 newFixedThreadPool
这个线程池简单了,就是开始我们就设定线程池的大小,只允许我们设置大小的线程数量存在。这个和 newSingleThreadExecutor 单线程线程池很类似,无非就是多几个线程,执行方式也和 newSingleThreadExecutor 没有太大区别。假设三个线程都处于工作状态,那后面来的任务就会进入队列中阻塞,等待线程空闲,当线程空闲了,就出队执行任务。
来看看该线程池的源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
这里,可以看到 corePoolSize 和 maximumPoolSize 都为我们设定的值,这就实现了线程池的定长。假设我们将值设置为 1,那就可以理解为这是个 newSingleThreadExecutor 线程池了。
/**
* @Auther: Gentle
* @Date: 2019/3/22 13:05
* @Description:
*/
public class CreateThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//构建定长线程池
ExecutorService executorService1 = Executors.newFixedThreadPool(3);
MyTask gentle = new MyTask("Gentle");
for (int i=0;i<15;i++) {
executorService1.execute(gentle);
}
executorService1.shutdown();
}
//无返回值任务
static class MyTask implements Runnable{
private String name;
public MyTask(String name){
this.name=name;
}
@Override
public void run() {
try {
//休眠 2 秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程名字:"+Thread.currentThread().getName() + " 传入名字:"+name);
}
}
}
根据代码运行发现执行结果只有三条线程在执行任务,定长线程池的只要合理的设置核心线程数量,可以很高效的执行任务,(这里不是动图,看不出什么。不过大家可以在自己编译器中测试,可以清楚的看出效果)
newScheduledThreadPool 线程池
该线程池具有有定时定期执行任务的功能,它可以实现 Timer 的定时功能,适用于一些指定时间才执行业务。
先看看该线程池的源码:
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
实质上,该线程池调用的也是 ThreadPoolExecutor 这个类。具体参数参考聊聊 java 线程池这篇文章。
下面我们来看一个 Demo:
/**
* @Auther: Gentle
* @Date: 2019/3/22 13:05
* @Description:
*/
public class CreateThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService1 = Executors.newScheduledThreadPool(3);
MyTask gentle = new MyTask("Gentle");
//延迟定时执行任务
((ScheduledExecutorService) executorService1).scheduleWithFixedDelay(gentle,1,2,TimeUnit.SECONDS);
MyTask gentle1 = new MyTask("Hello Gentle");
//延迟任务,延迟 3 秒后执行
((ScheduledExecutorService) executorService1).schedule(gentle1,3,TimeUnit.SECONDS);
}
//无返回值任务
static class MyTask implements Runnable{
private String name;
public MyTask(String name){
this.name=name;
}
@Override
public void run() {
System.out.println("线程名字:"+Thread.currentThread().getName() + " 传入名字:"+name);
}
}
}
上面代码中,用了该线程池的的两种形式,一种是延迟执行,一种是定时执行。下面我们看看执行结果:
这一篇文章对只是对四种线程池的基本用法做了简介。下一篇文章开始介绍各种线程池中队列的实现(这个是核心)。