Java 使用线程池

目录

一.介绍

二.ThreadPoolExecutor

  2.1 认识ThreadPoolExecutor

  2.2 ThreadPoolExecutor的构造方法列表

  2.3 任务队列分类

  2.4 线程工厂

  2.5 拒绝策略

三.使用Executors快速创建线程池

  3.1 Executors介绍

  3.2 newFixedThreadPool-固定线程数量的线程池

  3.3 newSingleThreadPool-只有一个线程的线程池

  3.4 newCachedThreadPool-缓存线程池

 

 

一.介绍

  本文主要介绍线程池的创建、线程任务队列分类以及线程池的扩展相关内容,只包含比较基础的知识,可以用来复习知识。

 

二.ThreadPoolExecutor

2.1 认识ThreadPoolExecutor

  ThreadPoolExecutor,可以理解为ThreadPool+Executor(线程池+执行器),执行器会使用线程池中的线程来处理提交的任务,可以看下面的继承关系图:

  

 

2.2 ThreadPoolExecutor的构造方法列表

  创建线程池,就是创建ThreadPoolExecutor实例对象,需要注意的是ThreadPoolExecutor有多个构造方法,如下所示:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

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

  corePoolSize:核心线程数(作用相当于一个门限值,看后面的介绍就知道了)

  maximumPoolSize:线程池中线程的最大数量;

keepAliveTime,TimeUnit:当线程池中的线程数量超过corePoolSize后,多余的空闲线程存活的时间,也就是超过corePoolSize的空闲线程在多长时间后会被销毁;

  workQueue:任务队列,用来保存已经提交但是并没有被执行的任务;

  threadFactory:线程池中线程的创建工厂(默认使用Executors.DefaultThreadFactory);

  handler:拒绝策略,当任务来不及被处理时被拒绝的处理操作;

 

2.3 任务队列分类

SynchronousQueue(同步队列):

ArrayBlockingQueue(有界队列)

LinkedBlockingQueue(无界队列)

PriorityBlockingQueue(优先队列)

DelayQueue

   DelayQueue是一个阻塞队列,用在定时周期任务中作为任务队列。

 

2.4 线程工厂

  熟悉设计模式的话,就知道XX工厂,就是用来创建XX的,所以ThreadFactory就是用来创建线程的,一般来说,我们直接使用默认的线程工厂即可,也就是Executors.DefaultThreadFactory;

  但是有时候我们需要对线程的创建做一些扩展,就可以通过对创建自己的线程工厂来实现,可以参考这篇博客,利用线程自定义线程工厂来对线程池中的线程进行命名的方式:Java的每个Thread都希望拥有自己的名称

  注意,可以通过继承ThreadPoolFactory来扩展线程池。

 

2.5 拒绝策略

  当任务不能提交到任务队列时,执行的操作,称为拒绝策略,JDK提供了4种拒绝策略(实现了RejectedExecutionHandler接口)。

  

  AbortPolicy:当不能向任务队列中提交任务时,就抛出异常,默认的拒绝策略;

  DiscardOldestPolicy:丢弃最早进入等待队列(还未来得及处理)的任务,然后让新的任务加入队列;

  DiscardPolicy:将新的任务直接丢弃;

  CallerRunsPolicy:交给当前线程进行处理任务;

  处理上面提供的拒绝策略,我们也可以根据自己的实际需求自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可,下面是一个简单示例:

package cn.ganlixin.thread;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
public class MyRejectedExecutionHandler implements RejectedExecutionHandler {

    /**
     * 拒绝策略处理逻辑
     *
     * @param task     提交到任务队列被拒绝的任务
     * @param executor 执行任务的执行器(线程池)
     */
    @Override
    public void rejectedExecution(Runnable task, ThreadPoolExecutor executor) {
        // 在这里定义逻辑,我这里只是输出一串文字
        log.error("提交任务被拒绝");
    }
}

  

  使用示例:

package cn.ganlixin.thread;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;

@Slf4j
public class TestThread {

    public static void main(String[] args) throws InterruptedException {

        Runnable runnable = () -> {
            Thread thread = Thread.currentThread();
            String name = thread.getName();
            log.info("thread {} running", name);

            try {
                TimeUnit.SECONDS.sleep(5); // 休眠5秒
            } catch (InterruptedException ignored) {
            }
        };

        int corePoolSize = 2;
        int maximumPoolSize = 4; // 最多4个线程
        int keepAliveSecond = 5;
        ExecutorService executorService = new ThreadPoolExecutor(
                corePoolSize, maximumPoolSize, keepAliveSecond, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2), // 任务队列容量为2
                Executors.defaultThreadFactory(),
                new MyRejectedExecutionHandler());

        // 提交8个任务,因为线程池最多有4线程,再加上2个任务队列的任务,所以有2个任务会被拒绝
        for (int i = 0; i < 8; i++) {
            executorService.submit(runnable);
        }
        
        // 输出如下:
        // thread pool-2-thread-3 running
        // thread pool-2-thread-2 running
        // thread pool-2-thread-4 running
        // thread pool-2-thread-1 running
        // 提交任务被拒绝
        // 提交任务被拒绝
        // thread pool-2-thread-4 running
        // thread pool-2-thread-2 running
        
        Thread.currentThread().join();
    }
}

  

三.使用Executors快速创建线程池

3.1 Executors介绍

  我们可以根据自己的实际需要来实例化ThreadPoolExecutor创建线程池,除此之外,Java中的Executors还提供了多个静态方法来创建各种类型的线程池(可以拿来即用)。

  可以利用Executors类的各个静态方法来创建线程池,创建的线程池特点从名称上就可以看出来,需要注意的这些静态方法内部其实也是利用ThreadPoolExecutor类来创建实例对象,只不过根据不同的类型设置了不同的参数。

  

 

3.2 newFixedThreadPool-固定线程数量的线程池

  固定线程数量的线程池,其实是在实例化ThreadPoolExecutor时,将corePoolSize和MaxPoolSize都设置为同一个值;

  需要这种方式创建的线程池使用的LinkedBlockingQueue作为任务提交队列,并且没有设定队列的容量,也就是说,如果任务没有来不及执行,就会将其提交到等待队列中(如果无限制的提交,将导致系统资源枯竭)。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}

  

3.3 newSingleThreadPool-只有一个线程的线程池

  创建只有1个线程的线程池,内部使用FinalizableDelegatedExecutorService进行了代理,当线程池中的线程被关闭后,会重新创建一个线程来继续工作。

  注意仍旧使用的未限制容量的LinkedBlockingQueue。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}

  

3.4 newCachedThreadPool

  创建缓存线程池,corePoolSize为0,maxPoolSize为Integer.MAX_VALUE(可以理解为不限制);

  线程在空闲60秒后就会被销毁;

  使用SynchronousQueue,也就是说,只要有新任务提交,如果有空闲线程,则让空闲线程处理任务;如果没有空闲线程,则会创建新线程来处理。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}

  

 

  

 

posted @ 2019-03-30 15:31  寻觅beyond  阅读(441)  评论(0编辑  收藏  举报
返回顶部