什么是线程池?
由于系统频繁的创建和销毁线程,因而使用线程池让创建的线程进行复用
5种线程池
- newFixedThreadTool():返回一个固定线程的线程池,该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有空闲的线程时,便处理在任务队列中的任务。
- newSingleThreadExecutor():返回一个只有一个线程的线程池,若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
- newCachedThreadTool():corePoolSize为0,maxmumPoolsize无穷大,没有任务时池内没有线程。任务被提交时,使用空闲的线程执行任务。若无空闲队列,使用SynchronousQueue迫使线程池加入新的线程执行任务。线程执行完毕,由于corePoolSize为0,空闲线程在指定时间内被回收。
- newSingleThreadScheduledExecutor():返回一个ScheduledExecutorService对象,线程池的大小为1.
- newScheduledThreadPool()不一定会立即执行任务,在指定时间,对任务进行调度。
- 使用自定义线程池
核心参数
1.corePoolSize:指定了线程池中的线程数量
2.maximumPoolSize:线程池中最大线程数量
3.keepAliveTime:当线程池中的线程数超过corePoolSize,多余的空闲的线程的存活时间。即,超过corePoolSize的空闲线程,在多长时间内,会被销毁。
4.unit:keepAliveTime的时间单位
5.workQueue:任务队列,被提交但是还没被执行的任务。是bockingQueue接口的对象,仅用于放置Runnable对象
- 直接提交的队列SynchronousQueue,没有容量,每一次插入都要等待一个相应的删除操作,反之,亦然。使用它,提交的任务不会被真实保存,总是将新任务提交给线程使用。如果没有空闲的进程,则尝试创建新的线程。如果进程数量已达最大,则执行拒绝策略。通常要设置很大的maximumPoolSize
- 有界的队列,可以使用arrayBlockingQueue,构造函数必须带有容量。当新的任务需要执行的时候,如果线程池的实际线程数小于corePoolSize,则会优先创建新的线程,如果大于corePoolSize,将多余的其加入等待队列。若等待队列已满,无法加入,且在总线程数不大于maxmiumPoolSize的情况下,创建新的线程执行任务。如果大于maxmiumPoolSize,执行拒绝策略。
- 无界队列:使用linkedBlockingQueue,与有界队列相比,除非资源耗尽,否则无界队列不存在任务入队失败的情况。
- 优先任务队列,PriorityBlockingQueue,带有执行优先级的队列,可以控制任务执行的先后顺序,也是无界队列。
6.threadFactory:线程工厂,用于创建线程,一般用默认的 最开始的线程由threadFactory调用newthread()创建
7.handler:拒绝策略,当太多任务来不及处理,如何拒绝任务。4种
- AbortPolicy:直接抛出异常,阻止线程正常启动。
- CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务,不会真的丢弃线程;如果执行程序已关闭,则会丢弃该任务
- DiscardOldestPolicy:丢弃最老的一个任务,也就是即将被执行的任务,并尝试再次提交当前任务。
- DiscardPolicy:丢弃无法处理的任务。如果任务允许丢失,这是最好的方案
- 还可以自己扩展RejectedExecutionHandler接口
线程池大小设定
设N为cpu的总核数
cpu密集(计算密集,大部分时间cpu在计算),线程池大小=N+1
IO密集(大部分时间线程在等磁盘读写),线程池大小=2N+1
原因:根据服务器性能优化公式估算,最佳线程数=(线程等待时间/ 线程cpu时间+1)*N
线程等待时间越高,需要越多线程;线程cpu时间越高,需要越少线程