java线程池

java线程池

线程池很久以前学习的知识了复习一下顺便做个总结

首先为什么需要有线程池这种东西

我们要知道java线程的创建和销毁是操作系统级别的操作,所以对于性能的消耗很大,如果频繁的创建销毁线程对于性能资源是一种很大的浪费,为了避免这种浪费,我们运用池化思想,使用线程池实现线程的复用来来避免线程的频繁创建和销毁。

使用线程池还能提高任务的响应速度,节约了创建线程的时间

并且,使用线程池让我们可以更好的管理线程,做出相应的优化和限制,避免一些由于无限制创建线程造成的系统问题

线程池的工作流程

摘自《java并发编程的艺术》

图解很清晰就不再赘述了

java为我们提供的一些线程池的实现

创建线程池主要有两种方式,一是自定义参数实现方式,二是使用Executors工厂方法创建java为我们提供的四种线程池,分别是

  1. FixedThreadPool(可重用固定线程数的线程池)

    使用无界队列,所有超过corePoolSize的线程都会加入到队列中,使用无界队列,队列不会满也就不会创建核心线程以外的线程了

  2. SingleThreadExecutor(使用单个worker线程的线程池)

    基本与FixedThreadPool一致,只是corePoolSize值和maximumPoolSize均设置为1

  3. CachedThreadPool(是一个会根据需要创建新线程的线程池)

    使用特殊队列SynchronousQueue即无法存储数据的队列,线程池表现出的特性为无核心线程,根据情况创建线程,如果线程池中还有存活的线程就直接执行任务,没有线程就创建线程执行任务,默认60s无任务销毁

  4. ScheduledThreadPool(能实现定时、周期性任务的线程池)

    它可另行安排在给定的延迟后运行命令,或者定期执行命令

    只是用了核心线程池

    任务队列是采用了DelayedWorkQueue去实现

自定义实现线程池

通过new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)自定义创建

想要自定义线程池首先我们要了解线程池的七大参数

  • corePoolSize:线程池保有的核心线程数。
  • maximumPoolSize:线程池创建的最大线程数。
  • keepAliveTime:非核心线程空闲存活时间
  • unit:keepAliveTime 的时间单位
  • workQueue:任务队列
  • threadFactory:线程工厂对象,可以自定义如何创建线程,如给线程指定name。
  • handler:自定义任务的拒绝策略。

前几个参数应该就不用多解释了,这里写一下任务队列、线程工厂和拒绝策略

任务队列
  1. ArrayBlockingQueue
  2. LinkedBlockingQueue
  3. PriorityBlockingQueue
  4. SynchronousQueue

关于这些阻塞队列的具体实现,推荐一篇博文我个人觉得写的非常好

线程池中的队列

线程工厂

其中的线程工厂,我们可以使用默认的DefaultThreadFactory或者自行实现ThreadFactory接口创建自定义实现,使用线程工厂的原因主要是了统一在创建线程时设置一些参数,如是否守护线程。线程一些特性等,如优先级。通过这个TreadFactory创建出来的线程能保证有相同的特性。ThreadFactory这个接口类方法只有一个,就是创建一个线程

拒绝策略

①CallerRunsPolicy

该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。

②AbortPolicy

该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。

③DiscardPolicy

该策略下,直接丢弃任务,什么都不做。

④DiscardOldestPolicy

该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

关于自定义线程池配置的一些配置技巧
首先我们要考虑我们的业务场景属于CPU密集型还是IO密集型

CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading很高。

在多重程序系统中,大部份时间用来做计算、逻辑判断等CPU动作的程序称之CPU bound。例如一个计算圆周率至小数点一千位以下的程序,在执行的过程当中绝大部份时间用在三角函数和开根号的计算,便是属于CPU bound的程序。

CPU bound的程序一般而言CPU占用率相当高。这可能是因为任务本身不太需要访问I/O设备,也可能是因为程序是多线程实现因此屏蔽掉了等待I/O的时间。

IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。

I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力。

那么通俗来讲就是,需要大量计算的且绝大部分性能压力压在CPU计算能力上的业务就是CPU密集型;而大部分小号时间和性能在IO上面的(如web应用程序,大部分时间在等待网络IO)的业务属于IO密集型,我们需要根据业务的特点来进行定制

一些常用的大小配置

CPU密集型的典型大小一般为CPU核心数-1、CPU核心数和CPU核心数+1。第一种参数主要考虑到我们的机器还需要处理除线程池任务以外的其他需求,为了平衡操作系统的压力使用核心数-1的数值;第三种考虑的是无需处理其他需求,但是由于线程切换或者任务中的等待会产生CPU空闲,为了更充分利用CPU使用核心数+1的大小;中间则为去两者的平衡。

而对于IO密集型,最常见的说法是使用2 * CPU核心数作为线程池的大小,但更严谨的说我们应该根据利特尔法则来判定线程池的大小,根据实际的业务压力来确定实际的大小

线程池大小 = ((线程 IO time + 线程 CPU time )/线程 CPU time )* CPU数目

当然,以上只是一些典型的配置,具体什么样的配置比较合适更应该通过实际的场景进行压力测试来判断

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值