说一说Java线程池

为什么使用线程池?

  • 重复利用现有的线程继续执行任务,避免创建与销毁所带来的开销
  • 由于没有创建与销毁线程的开销,所以会提高系统的响应速度
  • 通过线程池可以对线程进行合理的管理,根据系统的承受能力调整可运行线程的数量

启动线程数 = [任务执行时间 / (任务执行时间-IO等待时间)] * CPU内核数

ThreadPoolExecutor

newCacheThreadPool

  1. 不指定线程数,线程数最大值可以达到Integer.MAX_VALUE
  2. 线程可以缓存重复利用和回收(回收默认时间为1分钟)
  3. 当线程池中没有可用线程会创建一个线程

newFixedThreadPool

  1. 创建一个固定线程数并可重用的线程池,控制并发
  2. 线程可以重复使用,在显式关闭之前
  3. 所有的线程都处于BUSY的状态时,提交的任务必须在队列中等待

newSingleThreadExecutor

单个线程的线程执行器,线程处于BUSY的状态时,提交的任务必须在队列中等待

ScheduledThreadPool

newScheduledThreadPool

FixedThreadPool的基础上加了定时,延迟功能。

newSingleThreadScheduledExecutor

SingleThreadExecutor的基础上加了定时,延迟功能。

ForkJoinPool

CommonPool

newWorkStealingPool

创建一个带并行级别的线程池,并行级别决定同一时刻最多有几个线程同时执行,如不传入并发级别参数,将默认与当前系统的CPU个数相同。

线程池源码

ThreadPoolExecutor参数详解

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:
* {@code corePoolSize < 0}
* {@code keepAliveTime < 0}
* {@code maximumPoolSize <= 0}
* {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} or {@code handler} is null */
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { ··· ··· }

参数:

  • CorePoolSize 核心线程池线程数,核心线程池中的线程默认是一直开启的。
  • maxinumPoolSize允许的线程池,如果CorePool的线程都处于Busy状态,则有新的任务处理时,会开启一个新的线程。
  • keepAliveTime超出核心线程数的新创建线程保持存活的时间,在这段时间内会等待新的任务。
  • unit存活时间的时间单位。
  • workQueue这个队列将会保存Runnable任务;在执行方法execute(Runnable task)时,corePool已满,即正在执行的线程数等于corePoolSize时,被保存到此阻塞队列中等待执行。
  • threadFactory线程工厂,用于创建新的线程。
  • handler饱和策略,当前执行的任务数量大于maxinumPoolSize时,新的任务将会执行此策略。

阻塞队列

不同的线程池用到了不同的阻塞队列。

  • ArrayBlockingQueue 底层是数组,必须指定初始长度,生产者与消费者公用一把锁。
  • LinkedBlockingQueue底层是链表,不用指定初始长度,最大长度可达Integer.MAX_VALUE,生产者与消费者各用一把锁,生产者用PutLock,消费者用TakeLock,使得效率更高。
  • DelayQueue只有当元素的指定延迟时间到了,才能从队列中获取该元素,而且DelayQueue是一个没有大小限制的队列,因此,生产者不会被阻塞,消费者会被阻塞。
  • PriorityBlockingQueue基于优先级的的阻塞队列,根据自定义的Comparator接口来实现自定义排序,会根据优先级进行排序,底层数据结构是维护了一个二叉小顶堆实现。
  • SynchronousQueue同步队列,一种无缓冲的等待队列,消费者直接到生产者那边获取数据。

ArrayBlockingQueueLinkedBlockingQueue的区别?

  • 底层数据结构不同,前者使用数组,后者使用链表;前者需要有初始大小,后者没有,且最大长度可达Integer.MAX_VALUE
  • 效率不同,前者的生产者和消费者都共享同一把锁,后者生产者和消费者各用一把锁。

拒绝策略

  • AbortPolicy丢弃任务并抛出RejectedExecutionException异常。
  • DiscardPolicy丢弃任务不抛出异常。
  • DiscardOldestPolicy丢弃队列最前面的任务然后重新尝试执行任务。
  • CallerRunsPolicy由调用线程处理该任务。

线程池的生命周期

5种状态

  • Running:运行中,可以正常处理任务
  • Shutdown(执行Shutdown()方法):不再继续接受新的任务,只执行正在进行中的任务和等待队列中的任务,执行完之后进入Tidying状态
  • Stop(执行ShutdownNow()方法):不再继续接受新的任务,也不执行正在进行中的任务和等待队列中的任务,马上进入Tidying状态
  • Tidying:在进入此状态后调用terminated()方法,进入Terminated状态
  • Terminated:在terminated()执行完毕后进入该状态,在此状态中默认什么都没有做

与线程的生命周期区别?

线程生命周期的5种状态

  • 新生——new
  • 就绪——准备执行
  • 运行——running
  • 阻塞——wait()
  • 消亡——线程执行完毕

你可能感兴趣的:(JAVA,java,多线程,队列,链表,面试)