Java线程池相关

2019/03/09 JAVA

Java线程池相关

1.参数释义

Executor接口的实现类:ThreadPoolExecutor

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

corePoolSize :线程池的核心线程数量,核心线程即使在没有用的时候,也不会被回收;

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

keepAliveTime :线程池中除了核心线程之外的其他线程最长可以保留的时间;

util :计算这个时间的单位;

workQueue :等待队列,任务可以储存在任务队列中等待被执行,按照FIFO(先进先出)原则执行;

threadFactory :创建线程的线程工厂;

handler :拒绝策略,在任务满了之后,拒绝执行某些任务。

2.执行流程

任务进来时,首先判断核心线程是否处于空闲状态,如果有空闲,核心线程就先执行任务,如果核心线程已满,则判断任务队列是否有地方存放该任务,如果有,就将任务保存在任务队列中,等待执行,如果满了,再判断最大可容纳的线程数,如果没有超出这个数量,就开创非核心线程执行任务,如果超出了,就调用handler实现拒绝策略。

3.handler的拒绝策略

handler的拒绝策略有四种:

AbortPolicy: 不执行新任务,直接抛出异常,提示线程池已满;

DisCardPolicy: 不执行新任务,也不抛出异常;

DisCardOldSetPolicy: 将消息队列中的第一个任务替换为当前新进来的任务执行;

CallerRunsPolicy: 直接调用execute执行当前任务。

4.五种种线程池

newCachedThreadPool(): 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程;最大线程数为Integer.MAX_VALUE,容易造成内存泄漏

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

newFixedThreadPool(): 创建定长线程池,可控制最大并发数,超出的线程在队列种等待;不会复用线程,每运行一个Runnable都会通过ThreadFactory创建一个线程;如果定长的数量设置的过小,有可能造成任务堆积 ,最好根据系统资源进行设置,如Runtime.getRuntime().availableProcessors();没有设置超时时间,如果未关闭线程池,可能导致内存泄漏。

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

newScheduledThreadPool(): 创建定长线程池,支持定时及周期性任务执行。设计思想是:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。只有当任务的执行时间到来时,才会真正启动一个线程,其余时间都是在轮询任务的状态。

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(): 创建单线程化的线程池,只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。

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

newWorkStealingPool(): jdk1.8新增,根据所需要的并行层次动态创建和关闭线程,通过使用多个队列减少竞争,底层用ForkJoinPool 实现,可以充分利用多核cpu的优势,把一个任务拆分成多个小任务,多个小任务在多个处理器上并行执行,都执行完之后,再将这些执行结果合并起来。

public static ExecutorService newWorkStealingPool() {
    return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
                    ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                    null, true);
}

5.关闭线程池

线程池自动关闭的两个条件:线程池的引用不可达,线程池中没有线程。

如果没有关闭线程池,有可能导致内存泄漏。

关闭线程池可以调用shutdown()shutdownNow()方法。

关闭线程池操作时有几个问题:

1.是否可以继续执行新任务?继续提交新任务会怎样?

当线程池关闭后,继续提交新任务会执行拒绝策略,默认的拒绝策略是AbortPolicy,会抛出异常。

2.等待队列里的任务是否还会执行?

shutdown()方法关闭线程池,队列中的任务仍然会继续执行;如果希望等待队列中的任务不继续执行,可以使用shutdownNow()方法。

3.正在执行的任务是否会立即中断?

正在执行的任务是否会立即中断,首先要理解线程中的interrupt()方法,它在线程中设置一个中断标志,如果该线程被阻塞的话,则抛出异常InterruptedException

调用shutdown() 方法,线程池被关闭,正在运行的任务没有被中断;

调用shutdownNow()方法,正在运行的任务可以被中断。

参考:

深入理解线程和线程池(图文详解)

java8线程池

线程池的优雅关闭实践

Search

    Table of Contents