线程池ThreadPoolExecutor笔记

线程的创建方法

1、继承Thread类,没有返回值;
2、实现Runnable接口,没有返回值;
3、实现Callable接口通过FutureTask包装器来创建Thread线程,可以有返回值;
4、通过线程池实现,两种方法都提供。

线程池

一、通过Executors类提供的方法

  • 底层还是通过ThreadPoolExecutor构造方法实现,只是通过设置默认参数来实现。
1、newCachedThreadPool
  • 创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
public static ExecutorService newCachedThreadPool() {
    
    
     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                       60L, TimeUnit.SECONDS,
                                       new SynchronousQueue<Runnable>());
}

因为SynchronousQueue队列不保持它们,直接提交给线程,相当于队列大小为0,而最大线程数为Integer.MAX_VALUE,所以线程不足时,会一直创建新线程,等到线程空闲时,又有60秒存活时间,从而实现了一个可缓存的线程池。

2、newFixedThreadPool
  • 创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
    return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>());
}

因为核心线程数与最大线程数相同,所以线程池的线程数是固定的,而且没有限制队列的大小,所以多余的任务均会被放到队列排队,从而实现一个固定大小,可控制并发数量的线程池。

3、newScheduledThreadPool
  • 创建一个周期性的线程池,支持定时及周期性执行任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    
    
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    
    
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}

因为使用了延迟队列,只有在延迟期满时才能从中提取到元素,从而实现定时执行的线程池。而周期性执行是配合上层封装的其他类来实现的,可以看ScheduledExecutorService类的scheduleAtFixedRate方法。

注意:这里用的是ScheduledExecutorService类的schedule()方法,不是ExecutorService类的execute()方法。

4、newSingleThreadExecutor
  • 创建一个单线程的线程池,可保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public static ExecutorService newSingleThreadExecutor() {
    
    
    return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>()));
}

因为核心线程数与最大线程数相同,均为1,所以线程池的线程数是固定的1个,而且没有限制队列的大小,所以多余的任务均会被放到队列排队,从而实现一个单线程按指定顺序执行的线程池。

二、通过ThreadPoolExecutor类自定义

  • ThreadPoolExecutor类提供了4种构造方法,可根据需要来自定义一个线程池。
/**
 * @param corePoolSize    核心线程数,始终存活
 * @param maximumPoolSize 线程池中允许的最大线程数
 * @param keepAliveTime   存活时间,没有任务运行时存活的时间
 * @param unit            keepAliveTime的单位,
 * 						  TimeUnit枚举类,天、小时、分、秒、毫秒、微秒、纳秒
 * @param workQueue       一个阻塞队列,用来存储等待执行的任务,均为线程安全,7种可选。
 *                        较常用的是LinkedBlockingQueue和Synchronous。
 * 						  线程池的排队策略与BlockingQueue有关。 * 						  
 * @param threadFactory   线程工厂,主要用来创建线程,默及正常优先级、非守护线程。
 * @param handler         拒绝策略,拒绝处理任务时的策略,4种可选,默认为AbortPolicy。
 * @description ThreadPoolExecutor 线程池构造方法
 */
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                          BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    
    
    // 省略...
}

【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executors返回的线程池对象的弊端如下:
FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

在线程池中,当核心线程数不够时,工作任务会存入线程队列中;
当线程队列存满时,才会去判断最大线程数是否已满,再创建新的线程;
如果队列存满,当前工作线程数等于最大线程数,则会根据选择的拒绝策略执行。

猜你喜欢

转载自blog.csdn.net/qq_42080073/article/details/115051344