Java多线程 ThreadPoolExecutor示例

本文介绍了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过度使用等问题。
上一篇


推荐文章

评论
说点什么吧?

发表评论

取消回复
  最新文章