Home
img of docs

本文将介绍 Java 中线程池的实现方法,包括常见的线程池类型及其核心参数配置。

chou403

/ Thread

/ c:

/ u:

/ 8 min read


线程池有几种实现方法具体参数是什么

在Java中,线程池是通过java.util.concurrent包中的Executor框架实现的。Executor框架提供了多种线程池的实现,每种线程池都有不同的参数和用途。以下是几种常见的线程池实现及其具体参数:

1. ThreadPoolExecutor

这是最灵活和强大的线程池实现,可以通过构造函数来设置各种参数。

构造函数参数
   public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize: 核心线程数,即使在空闲时也会保留在线程池中的线程数。
  • maximumPoolSize: 线程池允许的最大线程数。
  • keepAliveTime: 当线程数超过核心线程数时,多余的空闲线程存活的时间。
  • unit: keepAliveTime 的时间单位。
  • workQueue: 用于存放等待执行任务的队列。
  • threadFactory: 用于创建新线程的工厂。
  • handle: 用于处理任务拒绝的策略。
线程池示例
   ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                   // corePoolSize
    10,                  // maximumPoolSize
    60,                  // keepAliveTime
    TimeUnit.SECONDS,    // unit
    new LinkedBlockingQueue<Runnable>(), // workQueue
    Executors.defaultThreadFactory(),    // threadFactory
    new ThreadPoolExecutor.AbortPolicy() // handler
);

2. Executors 工厂方法

Executors类提供了几种便捷的工厂方法来创建常用的线程池。

2.1. newFixedThreadPool(int nThreads)

创建一个固定大小的线程池。

固定大小线程池示例
   ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
2.2. newCachedThreadPool()

创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收线程则新建线程。

可缓存线程池示例
   ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
2.3. newSingleThreadExecutor()

创建一个单线程的线程池,它会确保所有任务按照指定的顺序(FIFO, LIFO, 优先级)执行。

单线程线程池示例
   ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
2.4. newScheduledThreadPool(int corePoolSize)

创建一个支持定时及周期性任务执行的线程池。

周期性线程池示例
   ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

3. 参数解释和选择

  • corePoolSize 和 maximumPoolSize: 决定线程池在不同负载下的行为。对于固定线程池,corePoolSizemaximumPoolSize 是相同的;对于缓存线程池,maximumPoolSizeInteger.MAX_VALUE
  • keepAliveTime 和 unit: 用于控制非核心线程的存活时间。
  • workQueue: 选择适当的队列类型(如LinkedBlockingQueue, SynchronousQueue)对任务的调度策略影响很大。
  • threadFactory: 允许自定义线程的创建方式,通常用于设置线程名称,优先级等。
  • handler: 当任务添加到线程池中被拒绝时的处理策略,如AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy

通过合理设置这些参数,可以创建满足不同并发需求的线程池,实现高效的任务处理。

threadlocal 是干嘛的如何实现的

ThreadLocal 是 Java 中的一种机制,允许你为每个线程创建独立的变量副本。这些变量副本对每个线程是隔离的,因此一个线程无法访问另一个线程的ThreadLocal变量。ThreadLocal常用于在多线程环境中存储不希望被共享的线程局部变量,例如用户会话,数据库连接等。

ThreadLocal的用途

  1. 线程安全: 避免多个线程访问共享变量导致的数据不一致和并发问题。
  2. 状态隔离: 为每个线程提供独立的变量副本,避免不同线程间的相互干扰。
  3. 简化代码: 简化了线程安全类的实现,无需显式使用锁机制。

ThreadLocal的实现

使用ThreadLocal非常简单,主要包括以下几个步骤:

  1. 创建ThreadLocal实例:

       private static final ThreadLocal<SimpleDateFormat> dateFormatHolder =
        new ThreadLocal<SimpleDateFormat>() {
            @Override
            protected SimpleDateFormat initialValue() {
                return new SimpleDateFormat("dd-MM-yyyy");
            }
        };
  2. 设置值:

       dateFormatHolder.set(new SimpleDateFormat("dd-MM-yyyy"));
  3. 获取值:

       SimpleDateFormat dateFormat = dateFormatHolder.get();
  4. 移除值(推荐使用,以避免内存泄漏):

       dateFormatHolder.remove();

TrealLocal 示例代码

以下是一个完整的示例,展示如何在多线程环境中使用ThreadLocal来存储和获取每个线程的独立变量:

   public class ThreadLocalExample {
    // 创建一个ThreadLocal变量,用于存储每个线程独立的SimpleDateFormat实例
    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("dd-MM-yyyy"));

    public static void main(String[] args) throws InterruptedException {
        // 创建多个线程,并启动它们
        Thread t1 = new Thread(() -> printDate("Thread-1"));
        Thread t2 = new Thread(() -> printDate("Thread-2"));
        Thread t3 = new Thread(() -> printDate("Thread-3"));

        t1.start();
        t2.start();
        t3.start();

        // 等待所有线程执行完毕
        t1.join();
        t2.join();
        t3.join();
    }

    private static void printDate(String threadName) {
        // 获取当前线程的SimpleDateFormat实例
        SimpleDateFormat dateFormat = dateFormatHolder.get();
        Date now = new Date();
        // 打印当前线程的日期格式
        System.out.println(threadName + ": " + dateFormat.format(now));
        // 移除当前线程的ThreadLocal变量,防止内存泄漏
        dateFormatHolder.remove();
    }
}

在这个示例中,每个线程都会创建自己的SimpleDateFormat实例并进行日期格式化操作,互不干扰,确保了线程安全。

ThreadLocal的工作原理

ThreadLocal的内部实现依赖于每个线程维护的一个ThreadLocalMap对象。每个ThreadLocal对象在ThreadLocalMap中都有一个独立的条目,该条目的键是ThreadLocal对象自身,值是实际的变量副本。

以下是ThreadLocal的工作流程:

  1. 存储值: 当调用ThreadLocal.set(T value)方法时,当前线程的ThreadLocalMap会将ThreadLocal对象作为键,value作为值存储起来。
  2. 获取值: 当调用ThreadLocal.get()方法时,当前线程的ThreadLocalMap会使用ThreadLocal对象作为键,查找对应的值并返回。
  3. 初始化值: 如果在调用ThreadLocal.get()方法时当前线程的ThreadLocalMap中不存在对应的条目,会调用initialValue()方法来初始化值并存储。
  4. 移除值: 调用ThreadLocal.remove()方法时,会从当前线程的ThreadLocalMap中移除对应的条目,以避免内存泄漏。

通过这种机制,ThreadLocal实现了每个线程独立的变量副本,确保了线程安全和数据隔离。