Java Thread Pool

一,线程池的好处

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

二,线程池使用方式

java中使用ThreadPoolExecutor来创建一个线程池。其基本用法如下:

 //创建一个线程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 5, 60,
            TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10),
            new ThreadPoolExecutor.CallerRunsPolicy());

//以execute的方式向线程池指定一个需要执行的任务
    threadPool.execute(new Runnable() {

        @Override
        public void run() {
            System.out.println("I am task One");
        }
    });

//以submit的方式向线程池指定一个需要执行的任务
    Future futureResult = threadPool.submit(new Runnable() {

        @Override
        public void run() {
            System.out.println("I am task Two");
        }

    });

值得注意的是:可以看到,向线程池中提交的任务都是实现了Runnable接口的

2.1 execute和submit提交的区别

execute:提交任务后,无返回值,任务的执行情况不可知
submit:提交任务后,返回一个future对象,可以通过它获得任务的相关执行情况,比如是否执行完毕,执行结果等。

三,线程池的原理

基本原理描述为,线程池中有多个工作线程worker,他们来实际执行交给线程池的任务,当提交的任务多于工作线程时,则将这些任务放入队列。工作线程会循环执行,在执行完一个任务后,便从队列中取出新的任务来执行。接下来详细说明。

3.1 线程池的构造参数

线程池完整的构造参数为:

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
                                ...
                              }

下面捡主要的说,

corePoolSize:线程池保有的基本工作线程数量,即便是空闲情况下也要保有工作线程的数量

maximumPoolSize:线程池允许的最大线程数量

keepAliveTime:当线程数量超过基本线程数量时,该时间用来指定空闲的线程再等待多长时间后会被干掉(为了将线程数维持在基本线程数量)

unit:设置上面keepAliveTime的时间单位

workQueue:不能被立即执行的Runnable任务,将被暂存在此处。对于线程调度要求的不同,可以使用不同的队列,比如保证公平性的队列,按优先级执行任务的优先级队列等。

threadFactory:用来创建工作线程的工厂

handler:当工作线程到达最大,且workQueue中也赛不下更多的任务时的处理器。(例如我们可以通过干handler来记录日志,告诉开发人员,任务太多了,超出线程池承受范围啦)

3.2 worker线程工作原理

worker线程其run方法如下:

    public void run() {
        try {
            Runnable task = firstTask;
            firstTask = null;
            while (task != null || (task = getTask()) != null) {
                runTask(task);
                task = null;
            }
        } finally {
            workerDone(this);
        }
    }

可以看到他是while循环从workQueue中获取task来执行。

四,工厂类

使用上述ThreadPoolExecutor的构造方法可以按需构造我们想要的线程池,但缺点是指定参数太多。JDK提供了一个工厂类Executors,他提供了很多方法直接得到具有某种特性的线程池实例,比如:

newCachedThreadPool():按需创建工作线程,适用于短时异步任务

newSingleThreadScheduledExecutor():只有一个线程的线程池,用来定时执行某任务

...等等

五,参考地址