Java中的线程池:ThreadPoolExecutor、ExecutorService

在Java中,线程池是一种常见的多线程编程技术,它可以有效地管理线程的创建和销毁,避免了频繁创建和销毁线程所带来的额外开销。本文将深入介绍Java中的线程池,涵盖ThreadPoolExecutor和ExecutorService两种线程池的使用方法、参数说明以及示例代码。


1. ThreadPoolExecutor

ThreadPoolExecutor是Java中内置的线程池实现类,它提供了丰富的配置选项,可以自定义线程池的核心线程数、最大线程数、阻塞队列、拒绝策略等参数。

1.1 创建ThreadPoolExecutor

下面是创建ThreadPoolExecutor的示例代码:

ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());

其中,corePoolSize是线程池的核心线程数,maximumPoolSize是线程池的最大线程数,keepAliveTime是非核心线程的空闲时间,TimeUnit是时间单位,LinkedBlockingQueue是阻塞队列的一种实现方式。

1.2 线程池的参数说明

ThreadPoolExecutor的参数说明如下:

  • corePoolSize:线程池的核心线程数,即线程池中能够同时执行的线程数。
  • maximumPoolSize:线程池的最大线程数,即线程池中最多能够同时执行的线程数。
  • keepAliveTime:非核心线程的空闲时间,即当线程池中的线程数大于核心线程数时,多余的空闲线程的存活时间。
  • unit:keepAliveTime的时间单位。
  • workQueue:阻塞队列,用于存储等待执行的任务。
  • threadFactory:线程工厂,用于创建新的线程。
  • handler:拒绝策略,用于处理无法处理的任务。

1.3 线程池的使用示例

下面是一个简单的线程池使用示例,它创建了一个线程池,向线程池中提交了10个任务,每个任务的执行时间为1秒。

public static void main(String[] args) throws InterruptedException {
    int corePoolSize = 2;
    int maximumPoolSize = 4;
    long keepAliveTime = 10;
    TimeUnit unit = TimeUnit.SECONDS;
    BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();
    ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);

    for (int i = 0; i < 10; i++) {
        executor.execute(new Task(i));
    }

    executor.shutdown();
    executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}

static class Task implements Runnable {
    private int taskId;

    public Task(int taskId) {
        this.taskId = taskId;
    }

    public void run() {
        try {
            System.out.println("Task-" + taskId + " start");
            TimeUnit.SECONDS.sleep(1);
            System.out.println("Task-" + taskId + " end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

上述示例中,核心线程数为2,最大线程数为4,阻塞队列为LinkedBlockingQueue,任务执行时间为1秒。通过execute方法向线程池中提交了10个任务,由于线程池中只有2个核心线程,因此前两个任务会立即执行,后续的任务会被加入到阻塞队列中等待执行。当阻塞队列满时,线程池会创建新的非核心线程执行任务,直到达到最大线程数为止。当线程空闲时间超过keepAliveTime时,非核心线程会被销毁。最后调用shutdown方法关闭线程池,并等待所有任务执行完成。


2. ExecutorService

ExecutorService是Java中提供的线程池框架,它提供了一些常用的线程池实现,如FixedThreadPool、CachedThreadPool、SingleThreadExecutor等,并且支持提交Callable和Runnable任务。

2.1 创建ExecutorService

下面是创建FixedThreadPool的示例代码:

ExecutorService executor = Executors.newFixedThreadPool(nThreads);

其中,nThreads是线程池的线程数。

2.2 ExecutorService的常用方法

ExecutorService提供了一些常用的方法,如submit、invokeAll、invokeAny、shutdown等。

  • submit方法:用于提交Callable或Runnable任务,并返回Future对象。
  • invokeAll方法:用于提交一组Callable任务,并返回Future列表,所有任务执行完成后才会返回。
  • invokeAny方法:用于提交一组Callable任务,并返回第一个执行完成的任务的结果。
  • shutdown方法:用于关闭线程池,不再接受新任务,并等待所有任务执行完成。

2.3 ExecutorService的使用示例

下面是一个使用FixedThreadPool的示例代码,它创建了一个FixedThreadPool,并向线程池中提交了10个任务,每个任务的执行时间为1秒。

public static void main(String[] args) throws InterruptedException, ExecutionException {
    int nThreads = 2;
    ExecutorService executor = Executors.newFixedThreadPool(nThreads);
    List<Future<Integer>> futures = new ArrayList<Future<Integer>>();

    for (int i = 0; i < 10; i++) {
        futures.add(executor.submit(new Task(i)));
    }

    executor.shutdown();
    executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

    for (Future<Integer> future : futures) {
        System.out.println(future.get());
    }
}

static class Task implements Callable<Integer> {
    private int taskId;

    public Task(int taskId) {
        this.taskId = taskId;
    }

    public Integer call() throws Exception {
        System.out.println("Task-" + taskId + " start");
        TimeUnit.SECONDS.sleep(1);
        System.out.println("Task-" + taskId + " end");
        return taskId;
    }
}

上述示例中,FixedThreadPool的线程数为2,任务执行时间为1秒。通过submit方法向线程池中提交了10个Callable任务,并将返回的Future对象存储到列表中。调用shutdown方法关闭线程池,并等待所有任务执行完成。最后遍历Future列表,获取每个任务的执行结果。


总结

本文深入介绍了Java中的线程池,涵盖了ThreadPoolExecutor和ExecutorService两种线程池的使用方法、参数说明以及示例代码。线程池是Java中常见的多线程编程技术,可以有效地管理线程的创建和销毁,避免了频繁创建和销毁线程所带来的额外开销。希望本文能够帮助读者更好地理解和应用线程池。

猿教程
请先登录后发表评论
  • 最新评论
  • 总共0条评论