线程池相关知识和调优

JUC中已经默认提供了一些种线程池给我们用,如果这些还不能达到我们的要求,我们也可以自己来创建线程池使用。

常见的Executors中的四种线程池

FixedThreadPool:这类线程池的特点就是里面全是核心线程,没有非核心线程,也没有超时机制,任务大小也是没有限制的,数量固定,即使是空闲状态,线程不会被回收,除非线程池被关闭,从构造方法也可以看出来,只有两个参数,一个是指定的核心线程数,一个是线程工厂,keepAliveTime无效。任务队列采用了无界的阻塞队列LinkedBlockingQueue,执行execute方法的时候,运行的线程没有达到corePoolSize就创建核心线程执行任务,否则就阻塞在任务队列中,有空闲线程的时候去取任务执行。由于该线程池线程数固定,且不被回收,线程与线程池的生命周期同步,所以适用于任务量比较固定但耗时长的任务。
CachedThreadPool:这类线程池的特点就是里面没有核心线程,全是非核心线程,其maximumPoolSize设置为Integer.MAX_VALUE,线程可以无限创建,当线程池中的线程都处于活动状态的时候,线程池会创建新的线程来处理新任务,否则会用空闲的线程来处理新任务,这类线程池的空闲线程都是有超时机制的,keepAliveTime在这里是有效的,时长为60秒,超过60秒的空闲线程就会被回收,当线程池都处于闲置状态时,线程池中的线程都会因为超时而被回收,所以几乎不会占用什么系统资源。任务队列采用的是SynchronousQueue,这个队列是无法插入任务的,一有任务立即执行,所以CachedThreadPool比较适合任务量大但耗时少的任务。
ScheduledThreadPool:这类线程池核心线程数量是固定的,好像和FixThreadPool有点像,但是它的非核心线程是没有限制的,并且非核心线程一闲置就会被回收,keepAliveTime同样无效,因为核心线程是不会回收的,当运行的线程数没有达到corePoolSize的时候,就新建线程去DelayedWorkQueue中取ScheduledFutureTask然后才去执行任务,否则就把任务添加到DelayedWorkQueue,DelayedWorkQueue会将任务排序,按新建一个非核心线程顺序执行,执行完线程就回收,然后循环。任务队列采用的DelayedWorkQueue是个无界的队列,延时执行队列任务。综合来说,这类线程池适用于执行定时任务和具体固定周期的重复任务。
SingleThreadPool:这类线程池顾名思义就是一个只有一个核心线程的线程池,从构造方法来看,它可以单独执行,也可以与周期线程池结合用。其任务队列是LinkedBlockingQueue,这是个无界的阻塞队列,因为线程池里只有一个线程,就确保所有的任务都在同一个线程中顺序执行,这样就不需要处理线程同步的问题。这类线程池适用于多个任务顺序执行的场景。
Java中提供创建线程池的类:Executors,上面四种线程池的创建就是Executors中的静态方法。如果我们想要创建自己的线程池,要使用ThreadPoolExecutor的构造方法创建。

//其它几个构造方法都会调用这个构造方法
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

参数含义:

  • corePoolSize:核心线程数量(最少线程数),在没有用的时候,也不会被回收
  • maximumPoolSize:线程池中可以容纳的最大线程的数量
  • keepAliveTime:程池中非核心线程最长可以保留的时间
  • util:非核心线程最长可以保留的时间的单位(TimeUnit的DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS)
  • workQueue:等待队列,任务可以储存在任务队列中等待被执行
  • handler:线程池的拒绝策略

常用的几种BlockingQueue接口的实现类:

  • ArrayBlockingQueue(inti):基于数组的阻塞队列实现,ArrayBlockingQueue内部维护了一个定长的数组,以便缓存队列中的数据对象,其内部没实现读写分离,也就意味着生产和消费者不能完全并行。长度是需要定义的,可以指定先进先出或者先进后出,因为长度是需要定义的,所以也叫有界队列,在很多场合非常适合使用。

  • LinkedBlockingQueue()或者(int i):基于链表的阻塞队列,同ArrayBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),LinkedBlockingQueue之所以能够高效地处理并发数据,是因为其内部实现采用分离锁(读写分离两个锁),从而实现生产者和消费者操作完全并行运行。

  • PriorityBlockingQueue()或者(int i):基于优先级别的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,也就是说传入队列的对象必须实现Comparable接口),在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁。

  • SynchronousQueue():一种没有缓冲的队列,生产者产生的数据直接会被消费者获取并且立刻消费。

  • DelayQueue:带有延迟时间的Queue,其中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue中的元素必须先实现Delayed接口,DelayQueue是一个没有大小限制的队列,应用场景很多,比如对缓存超时的数据进行移除、任务超时处理、空闲连接的关闭等等。

线程池的四种拒绝策略:(ThreadPoolExecutor的静态内部类)

  • AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满。默认策略
  • DisCardPolicy:不执行新任务,也不抛出异常。
  • DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行。
  • CallerRunsPolicy:直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。

线程池工厂:(Executors的静态内部类)
DefaultThreadFactory:默认缺省线程工厂,规定好了前缀名,不能设置守护线程。
PrivilegedThreadFactory: DefaultThreadFactory的子类,与class loader有关,后面再研究??????

线程池调优:
1、设置最大线程数和最小线程数。
2、选择合适的任务队列和拒绝策略。

在尝试获得最好的性能时,可以应用KISS原则"Keep it simple,stupid"。可以将最小线程数和最大线程数设置为相同。
在任务队列方面,如果适合无界队列,则选择LinkedBlockingQueue;如果适合有界队列,则选择ArrayBlockingQueue

线程池必须需仔细调优,盲目的向池中添加新线程,在某些情况下对性能会有不利的影响。
使用ThreadPoolExecutor时,选择更简单选项通常会带来最好的、最能预见的性能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值