目录
1、什么是线程池?
线程池是一种线程的使用模式。类似数据库连接池一样,线程可以通过线程池对系统使用的线程进行统一的管理。统一管理线程的创建,调度,销毁等等。
2、为什么使用线程池?
- 统一管理。一个功能复杂的系统中,势必需要使用多线程,一旦线程数量过多,就需要统一管理。如果不用线程池进行统一管理,那么就需要浪费大量的经历和无用的重复代码对线程进行管理。
- 内存节省。线程其实是系统中的稀缺资源,需要使用线程池对线程使用的内存开销进行管理,减少了每次创建线程、销毁线程的开销。
- 提高响应速度。任务提交给线程池之后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。由于线程的创建已经完成,故可以直接执行任务,因此提高了响应速度。
3、如何使用线程池?
3.1、创建线程池 -- new ThreadPoolExecutor();
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
- corePoolSize:线程池中常驻的线程数量,即使线程池是空闲的,也会有corePoolSize个线程。
- maximumPoolSize:线程池中允许的最大线程数。
- keepAliveTime:空闲线程最大空闲时间。当线程池中的线程超过corePoolSize个线程是,如果线程的空闲时间超过keepAliveTime就会把线程杀死。
- unit:线程池的时间。
- BlockingQueue:线程队列。
SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误
DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
ThreadFactory threadFactory:创建线程的方式,这是一个接口,你new他的时候需要实现他的Thread newThread(Runnable r)方法,
RejectedExecutionHandler handler: 当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理;jdk1.5提供了四种饱和策略 :AbortPolicy
默认。直接抛异常。
CallerRunsPolicy
只用调用者所在的线程执行任务,重试添加当前的任务,它会自动重复调用execute()方法
DiscardOldestPolicy
丢弃任务队列中最久的任务。
DiscardPolicy
丢弃当前任务。
package com.test;
import java.util.concurrent.*;
public class ThreadTest {
// 它是一种固定大小的线程池;
// corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads;
// keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效;
// 阻塞队列采用了LinkedBlockingQueue,它是一个无界队列;
// 由于阻塞队列是一个无界队列,因此永远不可能拒绝任务;
// 由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
// 它是一个可以无限扩大的线程池;
// 它比较适合处理执行时间比较小的任务;
// corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;
// keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
// 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>());
}
//它只会创建一条工作线程处理任务;
//采用的阻塞队列为LinkedBlockingQueue;
public static ExecutorService newSingleThreadExecutor(){
return new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
}
3.2、向线程池提交任务
public static void main(String[] args) {
Executor executor = ThreadTest.newCachedThreadPool();
executor.execute(new Runnable() {
int i = 0;
@Override
public void run() {
System.out.println(i++);
}
});
}
3.3线程池关闭
看了一下源码executor没有shutdown方式,需要转成ExecutorService。
void shutdown():不会立即终止线程池,首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程。此时,则不能再往线程池中添加任何新的任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。
List<Runnable> shutdownNow():将线程池的状态设置成SHUTDOWN状态,立即终止线程池,并且尝试打断正在运行的线程,清空缓存队列中的任务,返回尚未开始的任务。
它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow方法。
((ExecutorService) executor).shutdown();
更多内容请关注微信公众号“外里科技”
官方公众号 | 外里科技 |
运营公众号 | 英雄赚 |
微信 | wxid_8awklmbh1fzm22 |
1247408032 | |
开源代码 | https://gitee.com/B_T/beimi |