线程池(ThreadPoolExecutor)

目录

    什么是线程池?

怎么创建线程池?

ThreadPoolExecutor

线程池默认工作行为


什么是线程池?

    线程是操作系统进行资源分配和任务调度的最小单位,是计算机的宝贵资源,new thread(),调用start()方法java线程在创建和销毁的过程中消耗的资源是比较大的,为了减少在线程创建和销毁过程中的资源消耗就要实现线程的复用。在程序开发中池化技术是一种不错的复用方式,例如线程池、连接池、内存池等。我们会预先创建好一些昂贵的对象放入池中,在使用的时候直接获取,使用之后归还,还会通过一些策略调整池中对象数量实现池的动态伸缩。

怎么创建线程池?

    java的executors类库中提供了多种快速创建线程池的方法,例如newFixedThreadPool(),newCachedThreadPool()等,但是我们尽量不要直接使用,《阿里java开发规约》中也直接规定了不能使用executors类库中的创建线程池方法,应该手动使用newThreadPoolExecutor来创建线程池。这是因为newFixedThreadPool(),newCachedThreadPool()创建的线程池都是无界的,在线程大量堆积的时候就会容易造成OOM,翻开newFixedThreadPool()的源码不难发现线程池的工作队列直接new了一个LinkedBlockingQueue,而默认构造方法的LinkedBlockingQueue是Integer.MAX_VALUE长度的队列,是无界的,线程可以无限的堆积。


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

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    ...


    /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
...
}

翻开newCachedThreadPool的源码会发现它的最大线程数是Integer.MAX_VALUE,而工作队列 SynchronousQueue 是一个没有存储空间的阻塞队列,只要有请求就必须找到一个线程来处理,如果没有空闲线程就新创建一个,无限的创建线程也容易造成OOM。


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

根据具体的应用场景,我们需要清楚的知道我们所创建线程池的几个核心参数,包括:核心线程数、最大线程数、工作队列类型、拒绝策略、线程回收策略等以满足需求,一般需要可控的线程数和有界的工作队列。另外为了方便在线程池出现问题时(例如线程数暴增、线程死锁、占用大量CPU)方便定位我们需要为线程池定义一个有意义的名称。

ThreadPoolExecutor

构造方法参数:

public ThreadPoolExecutor(int corePoolSize, --核心线程数
                              int maximumPoolSize, --最大线程数
                              long keepAliveTime, --非核心线程数保留时间
                              TimeUnit unit, --keepAliveTime的时间单位
                              BlockingQueue<Runnable> workQueue, --工作队列
                              ThreadFactory threadFactory, --线程工厂
                              RejectedExecutionHandler handler --线程拒绝策略
)

参数说明:

1、corePoolSize:线程池核心线程数(平时保留的线程数)

2、maximumPoolSize:线程池最大线程数(当workQueue都放不下时启动新线程最大线程数)

3、keepAliveTime:超出corePoolSize数量的线程的保留时间。

4、unit:keepAliveTime单位

5、workQueue:阻塞队列,存放来不及执行的线程

ArrayBlockingQueue:构造函数一定要传大小

LinkedBlockingQueue:构造函数不传大小会默认为(Integer.MAX_VALUE ),当大量请求任务时,容易造成 内存耗尽。

SynchronousQueue:同步队列,一个没有存储空间的阻塞队列 ,将任务同步交付给工作线程。

PriorityBlockingQueue : 优先队列

6、threadFactory:线程工厂

7、handler:饱和策略

AbortPolicy(默认):直接抛弃

CallerRunsPolicy:用调用者的线程执行任务

DiscardOldestPolicy:抛弃队列中最久的任务

DiscardPolicy:抛弃当前任务

使用newThreadPoolExecutor创建线程池的示例:

//创建一个具有2个核心线程、5个最大线程,
//使用容量为10的ArrayBlockingQueue阻塞队列作为工作队列的线程池,
//使用默认的AbortPolicy拒绝策略
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
            2, 5,
            5, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            new ThreadFactoryBuilder().setNameFormat("demo-threadpool-%d").get(),
            new ThreadPoolExecutor.AbortPolicy());

线程池默认工作行为

1、不会初始化corePoolSize个线程,有任务来了才会创建线程。

2、当线程数超过corePoolSize时,不会先扩展线程数,而是将线程放在工作队列排队。

3、当工作队列中满了以后,扩展线程数最大到maximumPoolSize。

4、如果最大线程数达到maximumPoolSize以后还有线程进来按照拒绝策略处理。

5、超过核心线程数时,线程在等待keepAliveTime后仍没任务处理会被回收,线程池收缩至核心线程数。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值