在Java中,线程池是一种用于管理和复用线程的机制。它提供了一组线程,用于执行提交的任务,以减少线程的创建和销毁开销,并提高应用程序的性能和资源利用率。以下是线程池中的一些重要概念:
-
核心线程池(Core Thread Pool):核心线程池是线程池中最小的线程数量。在没有任务执行时,核心线程会一直保持活跃状态。核心线程不会被回收,除非线程池被关闭。
-
最大线程池(Maximum Thread Pool):最大线程池是线程池中最大的线程数量。当任务数量超过核心线程池的容量时,会先放入阻塞队列中,阻塞队列打满后,线程池会创建新的线程,直到达到最大线程池的容量。
-
任务队列(Task Queue):任务队列用于存储提交给线程池的任务。当线程池的线程都在执行任务时,新提交的任务会被存储在任务队列中等待执行。常见的任务队列有无界队列和有界队列。
-
拒绝策略(Rejected Execution Policy):当线程池无法接受新的任务时,就会触发拒绝策略。拒绝策略定义了如何处理被拒绝的任务。常见的拒绝策略有抛出异常、丢弃任务、丢弃队列中最旧的任务、将任务回退给调用者等。
-
线程工厂(Thread Factory):线程工厂用于创建线程。它提供了一种自定义线程创建的方式,可以设置线程的名称、优先级、是否为守护线程等。
-
线程池的生命周期(Lifecycle):线程池具有创建、运行、关闭等生命周期。在创建和初始化线程池后,可以提交任务并执行。当不再需要线程池时,应该显式地关闭线程池,释放资源。
在Java中的线程池实现中,常见的阻塞队列有以下几种:
-
ArrayBlockingQueue:基于数组的有界阻塞队列。它按照先进先出(FIFO)的顺序存储任务,当队列已满时,新的任务将被阻塞,直到有空间可用。
-
LinkedBlockingQueue:基于链表的可选有界或无界阻塞队列。它也按照先进先出(FIFO)的顺序存储任务,当队列已满时,新的任务将被阻塞,直到有空间可用。
-
SynchronousQueue:一个没有容量的阻塞队列。每个插入操作必须等待一个对应的删除操作,否则插入操作将被阻塞,反之亦然。这个队列主要用于传递任务的手-off。
这些阻塞队列在线程池中起到了重要的作用,它们具有以下特点:
-
有界与无界:ArrayBlockingQueue和LinkedBlockingQueue可以选择有界或无界的队列。有界队列可以防止无限制的任务提交,控制线程池的资源使用,但可能会导致任务被拒绝。无界队列可以无限制地存储任务,但可能会导致内存溢出。
-
公平与非公平:LinkedBlockingQueue支持公平性设置,可以按照任务的提交顺序执行任务。而ArrayBlockingQueue和SynchronousQueue则不支持公平性设置。
-
线程安全:这些阻塞队列都是线程安全的,多个线程可以同时对其进行插入和删除操作,而无需额外的同步措施。
-
阻塞操作:当队列满时,插入操作会被阻塞,直到有空间可用。当队列空时,删除操作会被阻塞,直到有任务可用。
ThreadFactory
是一个接口,用于创建线程的工厂。它定义了一个方法newThread()
,用于创建新的线程对象。
除了使用Executors.defaultThreadFactory()
获取默认的线程工厂,你也可以自定义实现ThreadFactory
接口来创建自己的线程工厂。
以下是几个常用的线程工厂实现:
-
Executors.defaultThreadFactory()
: 返回一个默认的线程工厂,用于创建具有默认设置的线程。 -
ThreadFactoryBuilder
(Guava库): 提供了一种链式调用的方式来创建自定义的线程工厂。
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("MyThread-%d")
.setPriority(Thread.MAX_PRIORITY)
.build();
在ThreadFactoryBuilder
中,setPriority()
方法用于设置线程的优先级。线程的优先级决定了线程在竞争CPU资源时的调度顺序。
Java中的线程优先级范围是1到10,其中1是最低优先级,10是最高优先级。默认情况下,线程的优先级是5。
通过调用setPriority()
方法,你可以设置线程的优先级。较高优先级的线程在竞争CPU资源时更有可能被调度执行,但并不能保证一定会先于优先级较低的线程执行。
在上面的示例中,通过setPriority(Thread.MAX_PRIORITY)
将线程的优先级设置为最高优先级。你也可以使用Thread.MIN_PRIORITY
或Thread.NORM_PRIORITY
来设置更低或默认的优先级。
请注意,线程的优先级设置可能受到操作系统和硬件的限制,不同操作系统和硬件平台可能对线程优先级的处理方式略有不同。因此,需要谨慎使用线程优先级,并且不应过度依赖于它来实现程序逻辑的正确性。
在Java中的线程池实现中,以下是几种常见的拒绝策略:
-
ThreadPoolExecutor.AbortPolicy
(默认):当线程池无法接受新任务时,抛出RejectedExecutionException
异常。 -
ThreadPoolExecutor.CallerRunsPolicy
:当线程池无法接受新任务时,直接在调用者线程中执行该任务。这样做可以降低提交任务的速度,但可以保证不会丢失任务。 -
ThreadPoolExecutor.DiscardPolicy
:当线程池无法接受新任务时,直接丢弃该任务,不做任何处理。该策略可能会导致任务丢失。 -
ThreadPoolExecutor.DiscardOldestPolicy
:当线程池无法接受新任务时,丢弃队列中最旧的任务(即等待时间最长的任务),然后尝试将新任务加入队列。
你也可以自定义拒绝策略,实现RejectedExecutionHandler
接口并重写rejectedExecution()
方法,根据自己的需求来处理无法接受的任务。