ThreadPoolExecutor用于管理和复用线程,能提高多线程应用程序的性能和资源利用率。

线程池创建

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolDemo {
public static void main(String[] args) {
    // 创建一个固定大小的线程池,大小为3
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        3, 3, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()
    );
    //注: LinkedBlockingQueue无界队列,如果队列变得太长,将会导致等待时间增加,系统负载加重,影响整体性能,
    // 可能会造成内存耗尽
    // 可以使用ArrayBlockingQueue 加拒绝策略来创建线程池
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        3, 3, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10), new DiscardOldestPolicy()
    );
    // 提交任务给线程池执行
    // 无返回值的直接execute就行 
    executor.execute(new Task("Task 1"));


    // 关闭线程池
    executor.shutdown();

    // 有返回值的用submit,Future对象用于接收返回结果
    Future<Integer> future = executor.submit(new Task());

    try {
        // 阻塞等待任务执行完成并获取结果
        int result = future.get();
        System.out.println("Task result: " + result);
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 关闭线程池
    executor.shutdown();
}

//若要线程有返回,实现callable接口
static class TaskWithReturn implements Callable<Integer> {
    public Integer call() {
        System.out.println("Executing task...");
        // 执行具体的任务逻辑,并返回结果
        // ...
        return 42;
    }
}

//若线程无返回,任务实现Runnable接口即可
static class Task implements Runnable {
    private String name;

    public Task(String name) {
        this.name = name;
    }

    public void run() {
        System.out.println("Executing task: " + name);
        // 执行具体的任务逻辑
        // ...
    }
}

static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        if (!executor.isShutdown()) {
            // 丢弃最旧的任务并尝试执行当前任务
            executor.getQueue().poll();
            executor.execute(r);
        }
    }
}
}

线程池工作流程

2023-09-22T03:03:44.png

引:[线程池工作流程][https://zhuanlan.zhihu.com/p/201427009]
注意:只有阻塞队列满了才开始创建救急线程。

核心线程数、最大线程数、阻塞队列大小的确定

  • 核心线程数是线程池中始终保持活动的线程数量。对于 CPU 密集型任务,可以将核心线程数设置比处理器核心数量稍多 T+1,以充分利用系统资源。
  • 对于 I/O 密集型任务,可以将核心线程数设置得更高,如2T,以处理可能的 I/O 阻塞导致的线程等待。
  • 核心线程数设置得太小,可能导致线程频繁创建和销毁,影响性能;设置得太大,可能会占用过多的系统资源。根据任务类型和系统负载,选择一个合适的值。
  • 最大线程数定义了线程池允许创建的最大线程数量。它通常要考虑系统资源以及对并发性能的需求。
  • 对于 CPU 密集型任务,最大线程数可以设置比处理器核心数量稍多。这样可以防止线程过多竞争 CPU 资源,避免线程切换的开销。
  • 对于 I/O 密集型任务,最大线程数可以设置得更高一些,以充分利用额外的线程来处理 I/O 阻塞。需要谨慎设置最大线程数,过多的线程可能导致系统资源消耗过高,线程间的竞争也可能降低性能。
  • 阻塞队列用于存储等待执行的任务。选择合适的阻塞队列大小有助于控制任务的提交速率和线程池的运行状态。
  • 对于有界阻塞队列,队列大小的选择要根据系统负载和内存限制来确定。如果任务提交速率高于线程池处理速度,可以增加队列的大小,以避免任务丢失。
  • 无界阻塞队列(如LinkedBlockingQueue)在一些情况下可以作为一种选择。但是要注意,如果任务的提交速率远远高于处理速度,无界队列可能会耗尽系统的内存
  • 平时开发的JavaWeb项目属于IO密集型