Skip to content

15_线程池

线程池是什么?

线程池是一种多线程处理形式。在任务执行过程中,将任务提交到线程池中,线程池中的线程会不断从队列中获取任务并进行执行。当一个任务完成后,该线程不会被销毁,而是返回到线程池中等待下一个任务的分配。

为什么要使用线程池?

  • 降低资源消耗:通过重复利用已创建的线程,避免频繁地创建和销毁线程所带来的系统开销。
  • 提高系统性能:合理控制同时执行的任务数量,防止过多线程导致的资源竞争和上下文切换问题。
  • 简化线程管理:统一管理和分配线程,减少手动管理多条独立线程的工作量。

线程池的创建方式及区别

Java 提供了 Executor 框架来实现任务执行服务。通过不同的工厂方法可以创建不同类型的线程池:

1. Fixed-size pool(固定大小线程池)

  • 使用 Executors.newFixedThreadPool(int nThreads) 创建。
  • 特点:
    • 线程数量固定,不会根据负载变化而调整。
    • 如果所有线程都在忙,则新任务会被排队等待执行。
  • 适用场景:适合处理任务数量有限、希望控制资源使用的情况。

2. Single thread pool(单个线程池)

  • 使用 Executors.newSingleThreadExecutor() 创建。
  • 特点:
    • 只有一个工作线程,所有提交的任务都会排队等待执行。
    • 如果这个唯一的线程被阻塞,则后续任务会按顺序等待直到该线程空闲。
  • 适用场景:适合需要顺序执行的任务。

3. Cached thread pool(可扩展线程池)

  • 使用 Executors.newCachedThreadPool() 创建。
  • 特点:
    • 线程数量动态调整,根据任务负载自动增加或移除线程。
    • 如果一个线程空闲了一段时间(默认是60秒),则会被回收和终止。
  • 适用场景:适合高并发、短时间的任务执行。

4. Scheduled thread pool(定时任务池)

  • 使用 Executors.newScheduledThreadPool(int corePoolSize) 创建。
  • 特点:
    • 支持定期或延迟任务的执行,类似于一个调度服务。
  • 适用场景:适合需要周期性检查或清理操作的任务。

示例代码

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

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建固定大小的线程池(Fixed-size pool)
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        
        // 提交任务到线程池
        for (int i = 0; i < 10; i++) {
            final int taskNumber = i;
            fixedThreadPool.execute(() -> {
                System.out.println("Task " + taskNumber + ": 正在执行");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Task " + taskNumber + ": 执行完毕");
            });
        }

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

        // 创建单个线程池(Single thread pool)
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        
        for (int i = 0; i < 3; i++) {
            final int taskNumber = i;
            singleThreadPool.execute(() -> {
                System.out.println("Task " + taskNumber + ": 正在执行");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Task " + taskNumber + ": 执行完毕");
            });
        }

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

        // 创建可扩展的线程池(Cached pool)
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        
        for (int i = 0; i < 15; i++) {
            final int taskNumber = i;
            cachedThreadPool.execute(() -> {
                System.out.println("Task " + taskNumber + ": 正在执行");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Task " + taskNumber + ": 执行完毕");
            });
        }

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

        // 创建定时任务池(Scheduled pool)
        ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
        
        Runnable runnable = () -> {
            System.out.println("定期任务:执行中...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("定期任务:完成");
        };

        // 每隔2秒执行一次
        scheduledThreadPool.scheduleAtFixedRate(runnable, 2, 2, java.util.concurrent.TimeUnit.SECONDS);

        try {
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

总结

  • Fixed-size pool:适合任务数量有限、需要控制资源使用的情况。
  • Single thread pool:适合顺序执行的任务,确保所有任务按顺序处理。
  • Cached pool:适合高并发、短时间的任务,能够快速响应新请求。
  • Scheduled pool:适合需要定期或延迟执行的任务。

选择合适的线程池类型取决于具体的业务场景和需求。