本文介绍了java多线程类ThreadPoolExecutor使用场景及实例
Java提供了ThreadPoolExecutor类,它是一种高度优化的多线程执行器,可以管理线程池、执行线程任务和控制线程池的大小和生命周期等
ThreadPoolExecutor类的参数
- corePoolSize:核心线程池大小,即线程池中始终存在的线程数量,除非设置了allowCoreThreadTimeOut参数,默认情况下,即使空闲,核心线程也不会被回收。
- maximumPoolSize:线程池的最大线程数,即可以同时执行的最大线程数量。
- keepAliveTime:非核心线程的空闲存活时间,当非核心线程空闲时间超过这个时间,就会被回收。
- unit:keepAliveTime的时间单位。
- workQueue:任务队列,用于存储等待执行的任务,有多种实现方式,例如ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等。
- threadFactory:用于创建新线程的工厂类,可以自定义线程名称、线程优先级等属性。
- handler:线程池的拒绝策略,当线程池已经达到最大线程数,并且任务队列已经满了,新的任务将被拒绝执行,可以设置拒绝策略来处理这种情况。
核心线程数和最大线程数设置
- CPU密集型任务:CPU密集型任务的特点是线程在执行任务时会一直利用CPU,对于这种情况要尽可能的避免发生线程上下文的切换。一般来说对于CPU密集型任务设置线程数为CPU核心数+1。
- IO密集型任务:线程在执行IO密集型任务时,可能大部分时间都浪费在阻塞IO上了,所以对于IO密集型任务来说我们通常会设置线程数为CPU核心数*2。不过这样子也不一定是最佳的,我们可以通过公式来进行计算:线程数 = CPU 核心数 *(1+平均等待时间/平均工作时间),尽可能的还要根据压缩来进行调整。
ThreadPoolExecutor实例
public class CustomThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池,大小为3,最大线程数为6,空闲线程存活时间为5秒,使用自定义线程工厂和拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 5, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10), new CustomThreadFactory(), new CustomRejectedExecutionHandler());
// 提交10个任务
for (int i = 0; i < 10; i++) {
executor.submit(new Task(i));
}
// 关闭线程池
executor.shutdown();
}
static class Task implements Runnable {
private int taskId;
public Task(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
System.out.println("Task " + taskId + " is running in thread " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " is done.");
}
}
static class CustomThreadFactory implements java.util.concurrent.ThreadFactory {
private int count = 1;
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("CustomThreadPool-" + count++);
return t;
}
}
static class CustomRejectedExecutionHandler implements java.util.concurrent.RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("Task " + ((Task) r).taskId + " is rejected.");
}
}
}
说明:创建了一个大小为3,最大线程数为6,空闲线程存活时间为5秒的线程池,任务队列的大小为10,使用了自定义的线程工厂和拒绝策略。然后提交了10个任务,每个任务输出了当前线程的名称,并休眠了3秒钟。当程序执行时,可能会出现任务被拒绝执行的情况,拒绝策略会输出任务被拒绝的信息。
ThreadPoolExecutor提供了两种执行任务的方法:
Future<?> submit(Runnable task)
void execute(Runnable command)
实际上submit中也是调用了execute方法
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
使用线程池的原因
- 线程创建和销毁的开销较大,每个线程都需要占用一定的内存和系统资源。如果频繁地创建和销毁线程,会导致系统的性能下降。
- 手动管理线程容易出现线程安全和资源竞争的问题,例如,多个线程同时访问共享变量可能导致数据不一致或者死锁等问题。
- 如果并发访问的线程数量很大,可能会导致系统资源不足,例如,内存不足或者CPU过度使用等问题。
发表评论
取消回复