java线程池ThreadPoolExecutor和阻塞队列BlockingQueue,Executor, ExecutorService

ThreadPoolExecutor

参数最全的构造函数

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

解释: corePoolSize 核心线程池大小, maximumPoolSize 大小,BlockingQueue 阻塞队列,多余的任务会放在这, ThreadFactory 生产线程池的线程, RejectedExecutionHandler 拒绝策略

  1. 线程池的工作流程, 当线程数小于corePoolSize时,每有新任务要做,则新创建一个线程, 当线程数达到corePoolSize时,就把任务放到workQueue中,如果人物队列满了,就再增加线程数,直到达到maximumPoolSize。
  2. 线程池的原理就是,创建线程后,这个线程不会因为执行玩任务而被关闭回收。 那么具体的原理就是,线程池中的线程执行的是一个while循环,每次从任务队列中取出任务,然后执行任务。核心代码:
    while (task != null || (task = getTask()) != null)的条件为真,第一次判断时task为firstTask,即执行的第一个任务,那么要点就成了getTask()必须不能为空

getTask()中有个核心逻辑是:

Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();

这里的workQueue就是阻塞队列,timed表示是否会超时释放,keepAliveTime是非核心线程允许的空闲时间;如果不超时,则调用BlockingQueue.take(),如果取不到值,就会一直阻塞直到程序提交了一个任务。所以,阻塞队列的作用是控制线程池中线程的生命周期。
所以阻塞队列保证了,线程池中的线程不会被回收,达到线程复用的目的。

  1. 为什么要使用拒绝策略? 有了阻塞队列后,为什么还会用拒绝策略呢,拒绝策略的场景就是就是线程数最大了,任务队列也满了,这个时候就需要拒绝策略了。 阻塞队列满了,就会阻塞了,为什么需要用拒绝策略呢?

因为添加任务调用的并不是阻塞的put()方法,而是非阻塞的offer()方法。 所以需要用拒绝策略。

所以线程池具有是生产者不阻塞,而消费者阻塞的特点。

BlockingQueue

  • 有界队列:
    ArrayBlockingQueue

  • 无界队列:
    LinkedBlockingQueue Executors.newFixedThreadPool() 采用的阻塞队列,因为是无界的,某些情况下会导致队列容量扩大,内存飙升
    同步移交队列:

  • SynchronousQueue 不存储元素,只是移交

  • 优先级队列:
    PriorityBlockingQueue 具有优先级

  • 延迟队列
    DelayedWorkQueue

4种不同的拒绝策略

4种策略都做为静态内部类在ThreadPoolExcutor中进行实现。

  • AbortPolicy中止策略
    该策略是默认饱和策略。
     public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                    throw new RejectedExecutionException("Task " + r.toString() +
                                                         " rejected from " +
                                                         e.toString());
         } 

使用该策略时在饱和时会抛出RejectedExecutionException(继承自RuntimeException),调用者可捕获该异常自行处理。

  • DiscardPolicy抛弃策略
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }

如代码所示,不做任何处理直接抛弃任务

  • DiscardOldestPolicy抛弃旧任务策略

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                  if (!e.isShutdown()) {
                      e.getQueue().poll();
                      e.execute(r);
                  }
      } 
    

如代码,先将阻塞队列中的头元素出队抛弃,再尝试提交任务。如果此时阻塞队列使用PriorityBlockingQueue优先级队列,将会导致优先级最高的任务被抛弃,因此不建议将该种策略配合优先级队列使用。

  • CallerRunsPolicy调用者运行

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
    r.run();
    }
    }

既不抛弃任务也不抛出异常,直接运行任务的run方法,换言之将任务回退给调用者来直接运行。使用该策略时线程池饱和后将由调用线程池的主线程自己来执行任务,因此在执行任务的这段时间里主线程无法再提交新任务,从而使线程池中工作线程有时间将正在处理的任务处理完成。

java 自带的4中线程池

都在Executors下面

  • newFixedThreadPool
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
  • newCachedThreadPool
 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue(),
                                      threadFactory);
    }
  • newScheduledThreadPool 使用的DelayedWorkQueue
public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
//
public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
    }

  • newSingleThreadExecutor 也就是核心容量为1且最大容量为1的FixedThreadPool
public static ExecutorService newSingleThreadExecutor() {
       return new FinalizableDelegatedExecutorService
           (new ThreadPoolExecutor(1, 1,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue()));
   }

Executor, ExecutorService, Executors的区别联系

下面列出了 Executor 和 ExecutorService 的区别:

Executor | ExecutorService
Executor 是 Java 线程池的核心接口,用来并发执行提交的任务 ExecutorService 是 Executor 接口的扩展,提供了异步执行和关闭线程池的方法
Executor提供execute()方法用来提交任务
ExecutorService提供submit()方法用来提交任务
Executor execute()方法无返回值
ExecutorService submit()方法返回Future对象,可用来获取任务执行结果
Executor 不能取消任务
ExecutorService 可以通过Future.cancel()取消pending中的任务
Executor没有提供和关闭线程池有关的方法
ExecutorService 提供了关闭线程池的方法

而Executors是一个类,工具类。Executors和Executor类似于Collections和Collection的关系。

参考链接:
https://segmentfault.com/a/1190000014417569

https://zhuanlan.zhihu.com/p/32867181
https://yemengying.com/2017/03/17/difference-between-executor-executorService/

你可能感兴趣的:(java)