线程池的基本使用

线程池的包

Excecutor 最初接口,只有excecute方法,这个方法只能放入Runnable
ExcecutorService 继承自Excecutor,有着更多的任务操作接口,比如取消,等待。还提供了submit方法,参数为Callable,返回类型为Future,用来获得返回值或异常
AbstractExecutorService 实现了ExcutorService,对线程池的基本实现,比如将submit进来的Callable给适配成Runnable来执行execute(),但并没有实现execute()
ThreadPoolExecutor 继承自AbstractExecutorService,可以对线程池进行参数配置,即七大参数,也是真正的用来实例化的线程池
Excecutors 快速创建线程池的类,创建的方法是给线程池的七大参数赋好对应值,然后调用new ThreadPoolExecutor

ThreadPoolExecutor的七大实例化参数

1 ThreadFactory 对线程做一些自定义工作,比如优先级,命名等,如果没有设置会使用默认的ThreadFactory
2 corePoolSize 核心线程数
3 maximumPoolSize 线程池最大线程数
4 keepAliveTime 非核心线程的最大空闲时,超过这段时间线程没有任务会被中断
5 unit 最大空闲时的时间单位
6 BlockingQueue 阻塞队列,放着等待执行的任务,从阻塞队列取出任务的顺序由阻塞队列的类型决定
7 RejectedExecutionHandler 拒绝策略,如果线程全部在工作,阻塞队列也满了,那么会根据拒绝策略拒绝线程

执行流程

如果一个任务被线程提交到线程池中,并且当前没有空闲线程可以处理该任务时
1 如果核心线程池没满,会创建新的核心线程完成工作
2 如果核心线程池已满,而阻塞队列没满,会塞入阻塞队列等待执行
3 如果核心线程池和阻塞队列都满了,会创建非核心线程完成工作
4 如果线程数已达到线程池最大线程数,会执行拒绝策略

阻塞队列

ArrayBlockingQueue:数组实现的公平队列
LinkedBlockingQueue:链表实现的公平队列,默认容量为Integer.MAX_VALUE,此时可以作为无界队列使用
Array和Linked的区别在于,Linked使用Node为节点,所以可以以头尾节点分别为锁进行出队和入队操作,但是Node节点的创建需要额外的消耗,而Array一开始就分配好了数组,没有创建,但是比较固定,需要一开始就确定需求容量。

PriorityBlockingQueue:堆实现的优先级队列,容量无限。
DelayQueue:PriorityBlockingQueue实现的有定时功能的队列,容量无限,超过时间才会出队,等待时间越长越在上面。需要自己实现比较器和getDelayed()唤醒方法。
这两种情况的堆实际上还是以数组的数据结构形式存在,所以是单锁。

SynchronousQueue:同步队列,分为公平和非公平,公平使用队列实现,非公平使用栈实现,无论是队列或栈都是用链表实现,所以容量无限,和其它队列不同的是,其它队列如果容量为1,那么塞满了会让线程执行拒绝策略,而这个有了一个任务等待,会把其它处于栈或队列的线程阻塞住,直到完成才唤醒下一个
LinkedTranserQueue
这两者都是自己的栈和队列来完成锁,然后容量为0

拒绝策略

拒绝策略会在关闭线程池后,或超过最大任务数(见执行流程)
线程池会调用RejectedExecutionHandler的rejectedExecution(),可以实现该接口来自定义拒绝策略
以下是几种常用的:
AbortPolicy:抛弃任务并报出异常
DiscardPolicy:抛弃任务但不报异常
DiscardOldestPolicy:抛弃排队最久的队列,非常不公平的做法
CallerRunsPolicy:由提交任务的外部线程自己完成

线程池的状态

Running 正在运行
Shutdown 停止提交,等待已提交任务完成
Stop 停止运行,并且将未完成任务清空
Tidying 没有任务
Terminated 所有任务都被清空,并且执行完最终的hook方法(这个方法是模板方法,可设定可不设定)

线程池的使用和关闭

实例化线程池后
使用submit(Callable/Runnable)或者execute(Runnable)就可以提交任务

shutdown()会拒绝继续提交任务给线程池,然后等待已有任务完成
shutdownNow()不但会拒绝,还会中断所有线程(等到它们阻塞时会报出interruptionException所以务必处理异常)并清空任务队列,将所有未完成的任务以列表形式返还
awaitTermination()会循环检查是否到达了Terminated状态,如果到达时限还没到达Terminated状态(可以看上一个目录线程池的状态),会返回false,否则返回true

参数和场景的确定

无论是线程数,还是拒绝策略,最好都自定义实现

I/O密集型:线程数一般和CPU一样多
CPU密集型:线程数一般为CPU*2
混合型:最佳线程数=((线程等待时间+线程CPU时间)/线程CPU时间)*CPU核数
由此可得:适合更多线程的,往往是I/O时间较长的,如RPC网络连接,Mysql。而像redis这种只和内存打交道,I/O时间短的更适合单线程

参考

《Java高并发核心编程》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值