线程池
创建线程池:
newCachedThreadPool-----》创建一个可缓存线程池,如果线程池长度超过需要处理需要,可灵活回收空线程,若无可回收,则新建线程。
newFixedThreadPool------>创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newSchedulThreadPool----》创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor----->创建一个单线程化的线程池,它会只用唯一的工作线程来执行任务,保证所有任务按照指定顺序FIFO、LIFO,优先级执行。
Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
corePoolSize & maximumPoolSize
当一个新任务被提交到池中,如果当前运行线程小于核心线程数(corePoolSize),即使当前有空闲线程,也会新建一个线程来处理新提交的任务;如果当前运行线程数大于核心线程数(corePoolSize)并小于最大线程数(maximumPoolSize),只有当等待队列已满的情况下才会新建线程。
等待队列
任何阻塞队列(BlockingQueue)都可以用来转移或保存提交的任务,线程池大小和阻塞队列相互约束线程池:
-
如果运行线程数小于
corePoolSize
,提交新任务时就会新建一个线程来运行; -
如果运行线程数大于或等于
corePoolSize
,新提交的任务就会入列等待;如果队列已满,并且运行线程数小于maximumPoolSize
,也将会新建一个线程来运行; -
如果线程数大于
maximumPoolSize
,新提交的任务将会根据拒绝策略来处理。
execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,Timeunit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
1. 返回值差异
- execute():定义在Executor接口中,仅接收Runnable任务,无返回值。适用于不关心执行结果的场景。 12
- submit():定义在ExecutorService接口中,支持Runnable和Callable任务,返回Future对象,可通过Future.get()获取任务结果或异常。 13
2. 异常处理机制
- execute():任务抛出异常时,异常会直接抛出到线程池的未捕获异常处理器(默认打印堆栈),可能导致线程终止。 24
- submit():异常会被捕获并封装到Future中,调用Future.get()时才会以ExecutionException形式重新抛出,需显式处理。 23
Executors创建
Executors创建常见有4种类型:
Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序。
Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。
1.FixedThreadPool(一池多线程)
创建一个固定线程数的线池,初始容量为5的线程池
public class fixThread {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);//设置线程初始容量为5
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "正在执行中");
});
}
}
}
2.newCachedThreadPool(可缓存线程池)
创建一个可缓存线程池,它的特点是根据需求创线程数,不需要指定数量
代码如下(示例):
//可缓存线程池
public class CacheThread {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i <10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"正在执行中");
});
}
}
}
3.newSingleThreadExecutor(一池一线程)
创建一个线程的线程池,不需要指定数量
public class fixThread {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i <10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"正在执行中");
});
}
}
}
4.newScheduledThreadPool(任务线程池)
public class fixThread {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i <10 ; i++) {
scheduledExecutorService.schedule(()->{
System.out.println(Thread.currentThread().getName()+"正在执行延迟任务");
},5, TimeUnit.SECONDS);
}
}
ThreadPoolExecutor
七大参数:
1,int corePoolSize, //核心线程数,线程池一直存在的线程数
2,int maximumPoolSize, //最大线程数,超过核心线程池另外运行存在的线程数
3,long keepAliveTime, //存活时间,最大线程数的不被使用的存活时间
4,TimeUnit unit,//存活单位
5,BlockingQueue<Runnable> workQueue) //工作队列
6,Executors.defaultThreadFactory()//线程工厂
7,defaultHandler 拒绝策略 任务超过线程可以处理的任务时候,采取的处理方式
public class fixThread {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,10,5,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5,true),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i <12 ; i++) {
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行中");
});
}
}
}
ThreadPoolExecutor 相比于其他创建线程池的优势在于,它可以通过参数来控制最大任务数和拒绝策略,让线程池的执行更加透明和可控,