【Java 中创建线程池的方式】

以下是一些常见线程池的创建方式:

1. 使用ThreadPoolExecutor自定义创建线程池

这是企业中最推荐的做法,因为它提供了最大的灵活性。

特性与特点:允许精确控制线程池的核心参数,如核心线程数、最大线程数、任务队列容量、线程空闲时间以及饱和策略等。

import java.util.concurrent.*;

public class CustomThreadPool {
    public static void main(String[] args) {
        // 自定义线程池配置
        int corePoolSize = 5; // 核心线程数
        int maximumPoolSize = 10; // 最大线程数
        long keepAliveTime = 60L; // 空闲线程存活时间
        TimeUnit unit = TimeUnit.SECONDS; // 时间单位
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(20); // 任务队列
        ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略
        
        ExecutorService executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );
        
        // 提交任务到线程池
        for (int i = 0; i < 15; i++) {
            executor.submit(() -> {
                System.out.println("Task executed by " + Thread.currentThread().getName());
            });
        }
        
        // 关闭线程池
        executor.shutdown();
    }
}

核心参数:

  • corePoolSize:核心线程数。
  • maximumPoolSize:最大线程数。
  • keepAliveTime:空闲线程存活时间。
  • workQueue:任务队列,用于存储等待执行的任务。
  • threadFactory:线程工厂,用于创建新线程。
  • handler:拒绝策略,当线程池无法处理新任务时采取的策略。

执行任务流程

  • execute(Runnable command):这是提交任务到线程池的主要方法。

其执行流程大致如下:

  1. 直接执行:如果当前运行的线程少于核心线程数,即使其他线程是空闲的,也会尝试直接创建一个新的线程来执行任务。
  2. 加入队列:如果当前线程数等于核心线程数,任务将被放入工作队列中排队。
  3. 扩容线程池:如果工作队列已满,且当前线程数小于最大线程数,会创建新的线程来处理任务。
  4. 拒绝策略:如果当前线程数已经达到最大值,且工作队列也满了,就会触发拒绝策略,通常是抛出异常或忽略该任务。

2. 使用Executors工厂方法

2.1. FixedThreadPool(固定大小线程池)

描述: 创建一个固定大小的线程池,该线程池可重用固定数量的线程。如果所有线程都在活动状态,新提交的任务将在队列中等待,直到有线程可用。
特点:线程池大小固定,所有线程都会一直存在直到程序结束,适合于执行大量相似任务的场景。
使用场景:适用于负载较稳定的场景,尤其是当任务数和线程处理能力相对均衡时。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>());
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小为5的线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);
        
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.execute(() -> {
                System.out.println("Task ID " + taskId + " is running in " + Thread.currentThread().getName());
            });
        }
        
        // 关闭线程池(最好在应用程序结束时执行)
        executor.shutdown();
        while (!executor.isTerminated()) {
            // 等待所有任务完成
        }
        System.out.println("Finished all threads");
    }
}

2.2. CachedThreadPool(可缓存线程池)

描述: 创建一个可根据需要创建新线程的线程池,线程池在需要时创建新线程,如果现有线程空闲超过60秒,则会被回收。
特点:线程池会根据需要创建新线程,如果有空闲线程则重用,否则新建。空闲线程在默认情况下存活60秒后被终止。
使用场景:适用于执行很多短期异步任务,如处理HTTP请求、执行短小的后台任务。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                60L, TimeUnit.SECONDS,
                                new SynchronousQueue<Runnable>());
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.execute(() -> {
                System.out.println("Task ID " + taskId + " is running in " + Thread.currentThread().getName());
            });
        }
        
        executor.shutdown();
    }
}

2.3. SingleThreadExecutor(单线程线程池)

描述: 创建一个只有一个线程的线程池,所有任务按照提交顺序依次执行。
特点:该线程池只使用一个线程来执行任务,任务按照提交顺序执行,适用于不需要并发但需要顺序处理的场景。
使用场景:适用于需要保证顺序执行任务的场景,或作为后台任务处理器。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                               0L, TimeUnit.MILLISECONDS,
                               new LinkedBlockingQueue<Runnable>()));
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.execute(() -> {
                System.out.println("Task ID " + taskId + " is running in " + Thread.currentThread().getName());
            });
        }
        
        executor.shutdown();
    }
}

2.4. ScheduledThreadPool(定时/周期性任务线程池)

描述: 创建一个支持定时及周期性执行任务的线程池。核心线程数固定,其余线程在无任务时会被回收。
特点:可以安排任务在未来的某个时间点执行,或者定期重复执行。适用于定时任务和周期性任务。
使用场景:执行定时任务,如定时数据同步、定时清理、定时检查等。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolExample {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
        
        Runnable task = () -> System.out.println("Task executed at " + System.currentTimeMillis());
        
        // 延迟5秒后执行一次
        executor.schedule(task, 5, TimeUnit.SECONDS);
        
        // 每隔3秒执行一次
        executor.scheduleAtFixedRate(task, 0, 3, TimeUnit.SECONDS);
        
        // 在关闭前,通常应有逻辑来决定何时停止调度
        // 这里为了简化,直接关闭线程池,但在实际应用中应避免立即关闭以免任务未完成
        executor.shutdown();
        while (!executor.isTerminated()) {}
        System.out.println("Finished all scheduled tasks");
    }
}

底层实现
上述所有Executors工厂方法创建线程池的实现最终都基于ThreadPoolExecutor类,它内部维护了工作线程集合、任务队列、同步控制锁等,以确保任务能够高效、安全地分配给线程执行,并处理线程的生命周期管理、任务调度和拒绝策略等问题。自定义配置线程池时,通过调整ThreadPoolExecutor的构造参数,可以针对特定应用场景优化性能和资源利用率。

特性总结

  • 灵活性: Executors提供的工厂方法简化了线程池的创建,使得开发者无需直接与ThreadPoolExecutor复杂的构造参数打交道。
  • 易用性: 提供了多种预设的线程池模型,满足不同场景下的需求。
  • 潜在风险: 尽管方便,但直接使用这些方法可能会导致资源管理不当(如无限增长的队列、过多线程),因此现代实践中更推荐直接使用ThreadPoolExecutor进行定制化配置,以更好地控制和理解线程池的行为。
  • 32
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值