Executors
Executors
是java.util.concurrent
包中提供的一个实用类,提供了一些工厂方法来创建线程池并返回相应的ExecutorService
实例。
-
newFixedThreadPool(int nThreads): 创建一个固定大小的线程池,池中的线程数量固定为
nThreads
。超过池大小的任务将在队列中等待。 -
newCachedThreadPool(): 创建一个可根据需要自动调整大小的线程池。池中的线程数量根据任务的数量动态调整。闲置线程会在60秒后终止。
-
newSingleThreadExecutor(): 创建一个使用单个工作线程的线程池,池中只有一个线程,任务按顺序执行。
-
newScheduledThreadPool(int corePoolSize): 创建一个固定大小的线程池,该线程池可用于定时任务和周期性任务调度。
-
newWorkStealingPool(): 创建一个工作窃取线程池,它根据需要创建和回收线程,可以充分利用多核处理器的计算能力。
ExecutorService
ExecutorService
是 Java 并发包中的一个接口,它是继承自Executor
接口的子接口,并提供了更多的方法来管理和控制任务的执行。ExecutorService
可以看作是一个管理线程池的服务。
-
submit(Runnable task): 提交一个
Runnable
任务给执行器执行,并返回一个Future
对象。通过Future
对象可以获取任务的执行结果或取消任务的执行。 -
submit(Callable<T> task): 提交一个
Callable
任务给执行器执行,并返回一个Future
对象。通过Future
对象可以获取任务的执行结果或取消任务的执行。 -
shutdown(): 优雅地关闭执行器,等待所有已提交任务执行完毕后再关闭。不再接收新的任务,但会将队列中的任务执行完毕。
-
shutdownNow(): 立即关闭执行器,尝试中断正在执行的任务,并返回尚未执行的任务列表。
-
awaitTermination(long timeout, TimeUnit unit): 阻塞当前线程,等待执行器的所有任务执行完毕或超时。返回
true
表示所有任务都已完成,false
表示超时。 -
isShutdown(): 判断执行器是否已关闭。
-
isTerminated(): 判断执行器的所有任务是否已经执行完毕。
-
invokeAny(Collection<? extends Callable<T>> tasks): 阻塞当前线程,执行给定的任务集合中的所有任务,并返回第一个成功执行的任务的结果。
-
invokeAll(Collection<? extends Callable<T>> tasks): 阻塞当前线程,执行给定的任务集合中的所有任务,并返回一个包含所有任务执行结果的
List<Future<T>>
对象。
ThreadPoolExecutor
ThreadPoolExecutor
是 Java 并发包中实现了ExecutorService
接口的一个具体类,它提供了一个可配置的线程池,用于执行提交的任务。
corePoolSize
: 线程池中的核心线程数,即保持活跃的线程数,即使没有任务需要执行。maximumPoolSize
: 线程池中允许的最大线程数,包括核心线程数和临时创建的线程数。keepAliveTime
: 当线程池中的线程数量超过核心线程数时,多余的空闲线程的最大存活时间。unit
:keepAliveTime
参数的时间单位。workQueue
: 用于保存等待执行的任务的阻塞队列。
四种拒绝策略
-
Abort Policy(默认): 当线程池无法处理新提交的任务时,会抛出
RejectedExecutionException
异常。 -
Caller Runs Policy: 当线程池无法处理新提交的任务时,会将该任务返回给调用者,由调用者所在线程执行该任务。
-
Discard Policy: 当线程池无法处理新提交的任务时,会直接丢弃该任务,不会抛出异常也不会执行该任务。
-
Discard Oldest Policy: 当线程池无法处理新提交的任务时,会先丢弃队列中最老的任务(即最先进入队列的任务),然后尝试再次提交新任务。
常见的任务队列
-
ArrayBlockingQueue: 这是一个基于数组的有界阻塞队列,它按照先进先出(FIFO)的顺序保存任务。当队列已满时,进一步的任务添加操作将会阻塞,直到队列中有空间可用。
-
LinkedBlockingQueue: 这是一个基于链表的可选有界阻塞队列,它按照先进先出(FIFO)的顺序保存。该队列可以在构造时指定一个容量值,如果未指定容量,则默认为
Integer.MAX_VALUE
。当队列已满时,进一步的任务添加操作将会阻塞,直到队列中有空间可用。 -
PriorityBlockingQueue: 这是一个支持优先级的无界阻塞队列,它按照元素的优先级进行排序并保存任务。调用队列中的
take()
方法可以获取优先级最高的任务。如果任务没有显式指定优先级,将会使用元素的自然顺序进行排序。 -
SynchronousQueue: 这是一个没有内部容量的阻塞队列,它在生产者线程将元素放入队列之前会一直等待消费者线程从队列中获取元素。在这种队列中,每个插入操作必须等待一个对应的删除操作,反之亦然。
这些任务队列类型适用于不同的场景和需求。ArrayBlockingQueue
和 LinkedBlockingQueue
是常用的队列实现,可以满足大多数情况下的需求。PriorityBlockingQueue
适用于需要按优先级顺序执行任务的场景。SynchronousQueue
适用于需要进行一对一数据交换的场景。
除了上述提到的队列,还有其他特定用途的任务队列,如 DelayQueue
(按延迟时间排序)、LinkedTransferQueue
(高并发场景下优化的队列)等。根据实际需求,选择合适的任务队列类型十分重要。
ThreadPoolExecutor
还提供了一些方法来获取线程池的状态和配置信息
getActiveCount()
: 获取当前活跃的线程数量。getCompletedTaskCount()
: 获取已完成的任务数量。getLargestPoolSize()
: 获取线程池中曾经同时存在的最大线程数量。getPoolSize()
: 获取线程池中当前线程数量。getQueue()
: 获取用于保存等待执行任务的队列。getTaskCount()
: 获取任务总数(已执行和未执行的任务)。
自定义线程池,封一个自定义线程池
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author : zzy
* @date : 2023/7/10 14:50
*/
public class MyThreadPool {
private ThreadPoolExecutor executor;
/**
* 构造方法,初始化线程池
*
* @param corePoolSize 核心线程数
* @param maximumPoolSize 最大线程数
* @param keepAliveTime 线程存活时间
* @param unit 时间单位
* @param workQueue 任务队列
* @param rejectPolicy Abort Policy(默认): 当线程池无法处理新提交的任务时,会抛出 RejectedExecutionException 异常。
* Caller Runs Policy: 当线程池无法处理新提交的任务时,会将该任务返回给调用者,由调用者所在线程执行该任务。
* Discard Policy: 当线程池无法处理新提交的任务时,会直接丢弃该任务,不会抛出异常也不会执行该任务。
* Discard Oldest Policy: 当线程池无法处理新提交的任务时,会先丢弃队列中最老的任务(即最先进入队列的任务),然后尝试再次提交新任务。
*/
public MyThreadPool(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程存活时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列
RejectedExecutionHandler rejectPolicy) {//拒绝策略
executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, rejectPolicy);
}
/**
* 默认构造方法,使用 AbortPolicy 作为拒绝策略
* @param corePoolSize 核心线程数
* @param maximumPoolSize 最大线程数
* @param keepAliveTime 线程存活时间
* @param unit 时间单位
* @param workQueue 任务队列
*/
public MyThreadPool(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new ThreadPoolExecutor.AbortPolicy());
}
/**
* 提交任务给线程池执行
*
* @param task 要执行的任务
*/
public void submitTask(Runnable task) {
executor.submit(task);
}
/**
* 关闭线程池
*/
public void shutdown() {
executor.shutdown();
}
/**
* 等待线程池终止执行
*
* @param timeout 等待超时时间
* @param unit 时间单位
* @throws InterruptedException 如果线程被中断则抛出该异常
*/
public void awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
executor.awaitTermination(timeout, unit);
}
/**
* 判断线程池是否已经终止
*
* @return 如果线程池已终止则返回true,否则返回false
*/
public boolean isTerminated() {
return executor.isTerminated();
}
}
多线程实例使用
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 自定义线程池参数
int corePoolSize = 2;
int maximumPoolSize = 4;
long keepAliveTime = 10;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2);
// 创建线程池
ThreadPoolExecutor.CallerRunsPolicy rejectPolicy = new ThreadPoolExecutor.CallerRunsPolicy();//拒绝策略
MyThreadPool executor = new MyThreadPool(corePoolSize, maximumPoolSize,
keepAliveTime, unit, workQueue, rejectPolicy);
// 提交任务给线程池执行
for(int i=0;i<100;i++){
executor.submitTask(new Task("Task"+i));
}
// 关闭线程池
executor.shutdown();
}
// 自定义任务类
static class Task implements Runnable {
private String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("Executing task: " + name + ", Thread: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);//模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
模仿调用接口访问
/**
* @author : zzy
* @date : 2023/7/11 10:34
*/
import java.util.concurrent.*;
public class ApiExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
// 提交包含接口调用的任务给线程池执行
Future<String> future = executor.submit(() -> {
// 调用阻塞的接口,这里假设调用了一个耗时的网络请求
String result = makeApiCall();
return result;
});
System.out.println(123);
// 主线程可以继续执行其他操作
try {
// 获取接口调用的结果,同时等待最多5秒钟
String result = future.get(5, TimeUnit.SECONDS);
System.out.println("接口调用结果:" + result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
// 在指定的时间内未能获取到结果,可以取消接口调用
future.cancel(true);
}
// 关闭线程池
executor.shutdown();
}
private static String makeApiCall() {
// 模拟一个耗时的接口调用
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "接口调用结果";
}
}