ExecutorService 是 Java 中一个用于管理线程池和并发任务执行的框架。它是 java.util.concurrent 包的一部分。ExecutorService 提供了一种比使用 Thread 类更方便和灵活的方式来管理线程。

1. 基本概念

1.1 Executor 和 ExecutorService
  • Executor:定义了一个 execute(Runnable command) 方法,用来执行提交的 Runnable 任务。
  • ExecutorService:继承了 Executor,提供了更多方法来管理和控制任务的执行,如 submit、shutdown、invokeAll 等。
1.2 线程池类型
  • FixedThreadPool:定长线程池,可以控制线程最大并发数,多余的线程会在队列中等待。
  • CachedThreadPool:可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • SingleThreadExecutor:单线程池,只有一个线程工作,确保所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
  • ScheduledThreadPool:定时线程池,用于执行定时任务或周期性任务。

2. 常用方法

2.1 任务提交方法
  • execute(Runnable command):执行一个 Runnable 任务,但无返回值。
  • submit(Runnable task):执行一个 Runnable 任务,并返回一个 Future 对象,可以通过 Future 对象获取任务的执行结果。
  • submit(Callable<T> task):执行一个 Callable 任务,并返回一个 Future 对象,Callable 有返回值。
2.2 任务调度方法(ScheduledExecutorService)
  • schedule(Runnable command, long delay, TimeUnit unit):延迟执行任务。
  • schedule(Callable<V> callable, long delay, TimeUnit unit):延迟执行任务并获取结果。
  • scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):周期性执行任务,以固定频率执行。
  • scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):周期性执行任务,以固定延迟执行。
2.3 关闭线程池
  • shutdown():启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
  • shutdownNow():试图停止所有正在执行的任务,暂停处理等待的任务,并返回等待执行的任务列表。

3. 使用示例

3.1 基本线程池示例

有5个任务需要执行。
使用一个固定大小为3的线程池来并发执行这些任务。
每个任务执行的过程包括打印任务开始的信息、模拟执行2秒、打印任务完成的信息。

public class BasicThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 1; i <= 5; i++) {
            executorService.submit(new Task(i));    //执行一个 Runnable 任务,并返回一个 Future 对象
        }

        executorService.shutdown();     //启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
    }
}

class Task implements Runnable {
    private final int taskId;

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

    @Override
    public void run() {
        System.out.println("第  " + taskId + " 线程, 线程名 " + Thread.currentThread().getName());
        try {
            Thread.sleep(2000); // 模拟任务执行时间
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("第  " + taskId + " 线程, 线程名 " + Thread.currentThread().getName()+" 结束时间  "+ LocalDateTime.now());
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

线程池中有3个线程 (pool-1-thread-1、pool-1-thread-2 和 pool-1-thread-3),每个线程可以同时执行一个任务。

前3个任务会立即被分配给线程池中的3个线程并开始执行。

每个任务执行2秒,然后完成,接着线程池中的线程会继续执行等待中的任务。

最终,所有任务都完成执行。
Java线程池ExecutorService学习和使用_java

3.2 使用 Callable 和 Future

如果需要每个任务执行之后需要返回一个结果,可以实现Callable接口并重写call方法

  1. 使用 Callable 接口和 Future 对象可以提交有返回值的任务,并获取任务执行结果。
  2. ExecutorService 提供了管理线程池和并发任务执行的能力。
  3. submit 方法用于提交 Callable 任务,Future 对象用于获取任务的结果或状态。
  4. 通过 future.get() 方法可以阻塞等待任务执行完成并获取结果。
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.concurrent.*;

public class CallableExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        ArrayList<Future<String>> resultList = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            Future<String> submit = executorService.submit(new CallableTask(i));
            resultList.add(submit);
        }
        executorService.shutdown();
        resultList.forEach(result -> {
            try {
                System.out.println(result.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
    }
}

class CallableTask implements Callable<String> {
    private final int taskId;

    CallableTask(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public String call() throws Exception {
        String threadName = Thread.currentThread().getName();
        System.out.println("第 "+taskId+" 个线程,线程名:" + threadName + " 开始时间:" + LocalDateTime.now());
        System.out.println("系统需要业务操作1秒");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "第 "+taskId+" 个线程,线程名:" + threadName + " 结束时间:" + LocalDateTime.now();
    }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.

Java线程池ExecutorService学习和使用_线程池_02

3.3 使用 ScheduledExecutorService 调度任务
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

        // 延迟5秒后执行任务
        scheduledExecutorService.schedule(new Task(1), 5, TimeUnit.SECONDS);

        // 每3秒执行一次任务
        scheduledExecutorService.scheduleAtFixedRate(new Task(2), 0, 3, TimeUnit.SECONDS);

        // 在上一个任务完成后3秒再执行
        scheduledExecutorService.scheduleWithFixedDelay(new Task(3), 0, 3, TimeUnit.SECONDS);

        try {
            Thread.sleep(15000); // 等待一段时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        scheduledExecutorService.shutdown();
    }
}

class Task implements Runnable {
    private final int taskId;

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

    @Override
    public void run() {
        System.out.println("第  " + taskId + " 线程, 线程名 " + Thread.currentThread().getName());
        try {
            Thread.sleep(2000); // 模拟任务执行时间
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("第  " + taskId + " 线程, 线程名 " + Thread.currentThread().getName()+" 结束时间  "+ LocalDateTime.now());
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.

4. 更多高级用法

4.1 使用 invokeAll 和 invokeAny
  • invokeAll:批量提交任务,并等待所有任务完成,返回所有任务的结果。
  • invokeAny:批量提交任务,并返回最先完成任务的结果。
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class AdvancedExecutorServiceExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        List<Callable<String>> tasks = Arrays.asList(
                new CallableTask(1),
                new CallableTask(2),
                new CallableTask(3)
        );

        try {
            // 批量提交任务并获取所有任务的结果
            List<Future<String>> futures = executorService.invokeAll(tasks);
            for (Future<String> future : futures) {
                System.out.println("线程结果: " + future.get());
            }

            // 批量提交任务并获取最先完成任务的结果
            String result = executorService.invokeAny(tasks);
            System.out.println("最先完成的是: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}
class CallableTask implements Callable<String> {
    private final int taskId;

    CallableTask(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public String call() throws Exception {
        String threadName = Thread.currentThread().getName();
        System.out.println("第 "+taskId+" 个线程,线程名:" + threadName + " 开始时间:" + LocalDateTime.now());
        System.out.println("系统需要业务操作1秒");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "第 "+taskId+" 个线程,线程名:" + threadName + " 结束时间:" + LocalDateTime.now();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.

转载自开思通智网: https://www.opensnn.com/os/article/10001031