java 线程池详解

为什么要使用线程池?

  • 为了减少资源的开销,提高系统性能
  • 先创建好线程,如果需要使用从池中取,提高利用率
  • 控制线程数量,防止程序崩溃

Java 中创建线程池的两种方式?

  1. 使用ThreadPoolExecutor类
  2. 使用Executors类 

其实这两种方式在本质上是一种方式,都是通过ThreadPoolExecutor类的方式

一、ThreadPoolExecutor的方式

1、使用方法

查看JDK的源码,ThreadPoolExecutor类提供了以下构造方法

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

   
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

构造方法参数说明:

  • corePoolSize:线程池的核心线程数
  • maximumPoolSize:线程池的最大线程数
  • keepAliveTime:线程池空闲时线程的存活时间
  • unit:线程池存活时间单位
  • workQueue:存放任务队列,使用的是阻塞队列
    • rrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
    • LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
    • PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
    • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
    • SynchronousQueue:一个不存储元素的阻塞队列。
    • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
    • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
    • 方法处理方式    抛出异常    返回特殊值    一直阻塞    超时退出
      插入方法           add(e)        offer(e)           put(e)       offer(e,time,unit)
      移除方法           remove()     poll()             take()        poll(time,unit)
      检查方法           element()    peek()           不可用       不可用
       
  • threadFactory:线程池创建线程的工厂
  • handler:在队列(workQueue)和线程池达到最大线程数(maximumPoolSize)均满时仍有任务的情况下的处理方

四个拒绝策略:

ThreadPoolExecutor.AbortPolicy():丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy():丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy():丢弃队列最前面的任务,然后重新提交被拒绝的任务

ThreadPoolExecutor.CallerRunsPolicy():由调用线程( 直接调用run方法并且阻塞执行)处理该任务

查看java.util.concurrent.ThreadPoolExecutor类的源码:


    /**
     * The default rejected execution handler
     */
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

线程池的默认拒绝策略为AbortPolicy,即丢弃任务并抛出RejectedExecutionException异常。测试代码:

ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
        ThreadFactory factory = f-> new Thread(f,"thread-pool-test");
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
                0L, TimeUnit.SECONDS, queue, factory);
        while (true){
            executor.submit(()->{
                System.out.println(queue.size());
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

线程池拒绝策略DiscardPolicy,丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。

使用此策略,可能会使我们无法发现系统的异常状态。建议是一些无关紧要的业务采用此策略。

测试代码:

 ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
        ThreadFactory factory = f-> new Thread(f,"thread-pool-test");
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
                0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.DiscardPolicy());
        while (true){
            executor.submit(()->{
                System.out.println(queue.size());
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

线程池拒绝策略DiscardOldestPolicy,丢弃队列最前面的任务,然后重新提交被拒绝的任务。

此拒绝策略,是一种喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量

测试代码:

 ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
        ThreadFactory factory = f-> new Thread(f,"thread-pool-test");
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
                0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.DiscardOldestPolicy());
        while (true){
            executor.submit(()->{
                System.out.println(queue.size());
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

线程池拒绝策略CallerRunsPolicy,由调用线程处理该任务

如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务,我们可以通过代码来验证这一点:

测试代码:

 ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
        ThreadFactory factory = f -> new Thread(f, "thread-pool-test");
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
                0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 1000; i++) {
            executor.submit(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + ":执行任务");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

通过结果可以看到,主线程main也执行了任务,这正说明了此拒绝策略由调用线程(提交任务的线程)直接执行被丢弃的任务的。

二、Executors的方式

1、使用方法

Executors类提供了下面的构造方法,

 

前面说Executors类通过ThreadPoolExecutor类的方式实现的,但是我们看到的返回值类型确实ExecutorService,这不是我们要的ThreadPoolExecutor,那么我们查看下他们之间的关系

可以看到ThreadPoolExecutor继承了AbstractExecutorService,AbstractExecutorService抽象类实现了ExecutorService接口,那么ThreadPoolExcutor和ExecutorService就有了关系。

那么我们在查看ExecutorService 的具体实现:

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

可以看到返回的是ThreaPoolExecutor对象,调用的是ThreaPoolExecutor类的构造方法。

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

newFixedThreadPool 创建一个定长线程池,可控制线程并发数,超过的线程会在队列中等待。

ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    System.out.println(sdf.format(date) + "---"+ Thread.currentThread().getName() + "   i=" + temp);
                }
            });
        }

执行结果:

2020-01-09 15:47:20---pool-1-thread-2   i=1
2020-01-09 15:47:20---pool-1-thread-1   i=0
2020-01-09 15:47:20---pool-1-thread-3   i=2
2020-01-09 15:47:21---pool-1-thread-2   i=3
2020-01-09 15:47:21---pool-1-thread-1   i=4
2020-01-09 15:47:21---pool-1-thread-3   i=5
2020-01-09 15:47:22---pool-1-thread-1   i=7
2020-01-09 15:47:22---pool-1-thread-3   i=8
2020-01-09 15:47:22---pool-1-thread-2   i=6
2020-01-09 15:47:23---pool-1-thread-1   i=9

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

public class Test {

    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(sdf.format(new Date()) + "-----" + Thread.currentThread().getName()+"---" + "HeartBeat.........................");
            }
        }, 5, 3, TimeUnit.SECONDS);   //5秒后第一次执行,之后每隔3秒执行一次

    }
}

执行结果:

2020-01-09 16:13:53-----pool-1-thread-1---HeartBeat.........................
2020-01-09 16:13:56-----pool-1-thread-1---HeartBeat.........................
2020-01-09 16:13:59-----pool-1-thread-2---HeartBeat.........................
2020-01-09 16:14:02-----pool-1-thread-1---HeartBeat.........................
2020-01-09 16:14:05-----pool-1-thread-3---HeartBeat.........................
2020-01-09 16:14:08-----pool-1-thread-3---HeartBeat.........................
2020-01-09 16:14:11-----pool-1-thread-3---HeartBeat.........................
2020-01-09 16:14:14-----pool-1-thread-3---HeartBeat.........................

newSingleThreadExecutor 创建一个单线程化的线程池,它会用唯一的工作线程来执行任务,保证任务按照先进先出的顺序执行。

public class Test {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "   i=" + temp);
                }
            });
        }
    }
}

执行结果

pool-1-thread-1   i=0
pool-1-thread-1   i=1
pool-1-thread-1   i=2
pool-1-thread-1   i=3
pool-1-thread-1   i=4
pool-1-thread-1   i=5
pool-1-thread-1   i=6
pool-1-thread-1   i=7
pool-1-thread-1   i=8
pool-1-thread-1   i=9

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值