java基础之java线程池

4:Thread第三部分线程池

ThreadPoolExecutor 是 Java 提供的一个线程池类,可以实现多线程并发处理。它提供了多种可配置的构造参数,以满足不同的业务需求。下面给出一个示例代码,并详细解释一下 ThreadPoolExecutor 构造参数的含义和作用:

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        int corePoolSize = 3; // 线程池核心线程数
        int maximumPoolSize = 5; // 线程池最大线程数
        long keepAliveTime = 60L; // 非核心线程数闲置超时时间,单位为秒
        int queueSize = 10; // 任务队列大小

        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>(queueSize));

        for (int i = 1; i <= 10; i++) {
            final int taskId = i;
            Runnable task = () -> {
                System.out.println("Task #" + taskId + " is running by "
                        + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // 模拟任务执行时间,单位为毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Task #" + taskId + " is completed by "
                        + Thread.currentThread().getName());
            };
            executor.execute(task);
        }
        executor.shutdown();
    }
}

在上述代码中,我们首先定义了线程池的核心线程数 corePoolSize、最大线程数 maximumPoolSize、非核心线程闲置超时时间 keepAliveTime 和任务队列大小 queueSize,然后使用这些参数创建了 ThreadPoolExecutor 实例。

接下来,在 for 循环中向线程池中提交 10 个任务。每个任务会输出自己的编号和执行线程的名字,然后模拟一个耗时 1 秒的任务执行过程,并在任务完成后再输出一条消息。

ThreadPoolExecutor 构造参数的具体含义如下:

  • corePoolSize:线程池的核心线程数,即能同时执行的任务数量;
  • maximumPoolSize:线程池的最大线程数,即所有线程池中线程数的上限;
  • keepAliveTime:非核心线程数闲置超时时间,即超过核心线程数以外的线程闲置超过一定时间后会被销毁,以节省系统资源;
  • unit:keepAliveTime 参数的时间单位,例如 TimeUnit.SECONDS 表示 keepAliveTime 是以秒为单位的;
  • workQueue:任务队列,用于存放未执行的任务;
  • threadFactory:线程工厂,用于创建新线程;
  • handler:饱和策略,当任务队列已满且没有空闲线程时,新提交的任务应该采取怎样的处理方式。

其中,workQueue 和 handler 的类型分别是 BlockingQueue 和 RejectedExecutionHandler,具体实现类可以根据业务需求进行选择。例如,LinkedBlockingQueue 和 ArrayBlockingQueue 都是阻塞队列的实现,但前者具有无限容量,而后者则需要指定固定容量。而 CallerRunsPolicy 策略会将任务交给当前线程来执行,这样可能会对系统造成更大的负担,可以使用 DiscardPolicy 或其他合适的策略来避免这种情况。

4.1、任务队列

任务队列是用于存放还未执行的任务的数据结构,它通常是一个阻塞队列,当线程池中所有的核心线程都在忙碌处理任务时,新提交的任务就会被暂存在任务队列中等待执行。Java 提供了多种类型的阻塞队列,常见的有以下几种:

  • ArrayBlockingQueue:一个由数组结构组成的有界队列,按照先进先出的原则对元素进行排序。
  • LinkedBlockingQueue:一个由链表结构组成的有界(默认大小是 Integer.MAX_VALUE)队列,按照先进先出的原则对元素进行排序。
  • SynchronousQueue:一个不存储元素的队列,每个插入操作必须等待另一个线程调用移除操作,否则插入操作一直处于阻塞状态(直到队列中已有空闲的线程为止)。
  • PriorityBlockingQueue:一个支持优先级排序的无界队列。

ArrayBlockingQueue 和 LinkedBlockingQueue 都是基于内存数组和链表实现的有界队列,其中 LinkedBlockingQueue 默认大小为 Integer.MAX_VALUE,意味着队列几乎是无界的。这两种队列的主要区别在于内部数据结构不同,ArrayBlockingQueue 内部是一个数组,而 LinkedBlockingQueue 内部是一个链表。

SynchronousQueue 是一个不存储元素的队列,每次插入操作必须等待另一个线程调用移除操作。这种队列只有在提交给线程池的任务被立即执行时才会被使用。由于 SynchronousQueue 没有任何容量,因此任务无法排队,如果存在空闲线程则直接将任务交给该线程执行,否则就创建一个新线程来执行任务。

PriorityBlockingQueue 是支持优先级排序的队列,它可以根据元素的优先级进行排序,但是并没有固定大小限制,也不存在任何容量或者阻塞性质,因此需要特别注意可能会出现内存溢出的问题。

4.2、线程工厂

线程工厂是用于创建新线程的对象,它提供了一种将线程的创建和线程池的实现分离的方式。通过自定义线程工厂,我们可以更方便地对线程进行管理和监控,并且可以为线程设置名字、优先级等属性。Java 提供了一个 ThreadFactory 接口,用于定义生产线程的规范,常见的实现类有以下几种:

  • Executors.defaultThreadFactory() :返回一个由系统默认提供的线程工厂创建的新线程。
  • ThreadFactoryBuilder :基于构建者模式实现的自定义线程工厂,可以灵活设置线程名前缀、是否守护进程等属性。
  • NamedThreadFactory:一个通用的线程工厂实现类,支持自定义线程名和优先级。

4.3、饱和策略

饱和策略是线程池在无法处理新提交的任务时采取的一种策略,Java 提供了 4 种饱和策略,分别是:

  • AbortPolicy :直接抛出异常,默认策略;
  • CallerRunsPolicy :只用调用者所在线程来运行任务;
  • DiscardOldestPolicy :丢弃队列中最老的任务,并尝试重新提交当前任务;
  • DiscardPolicy :直接丢弃任务。

以上这四种策略都实现了 RejectedExecutionHandler 接口,可以根据实际业务需求选择合适的策略。通常来说,如果不想丢失任何任务,可以使用 CallerRunsPolicy 策略;如果对性能有要求,应该控制队列大小并使用 DiscardPolicy 或 DiscardOldestPolicy 策略来丢弃一定量的任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值