面试准备 -- 四种线程池基本用法与解析

说线程池队列之前,还需要先知道如何构建线程池。JDK 已经为我们提供了创建线程池的工具类了,只需要我们调用即可。下面我们看看这个工具类提供的几种线程池:

  1. newCachedThreadPool
    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  2. newFixedThreadPool
    构建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  3. newScheduledThreadPool
    构建一个定长线程池,支持定时及周期性任务执行。

  4. 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);
        }
    }
}

上面代码中,用了该线程池的的两种形式,一种是延迟执行,一种是定时执行。下面我们看看执行结果:
在这里插入图片描述
这一篇文章对只是对四种线程池的基本用法做了简介。下一篇文章开始介绍各种线程池中队列的实现(这个是核心)。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值