线程池
概念
Java中的线程池(juc包下)是运用场景多的并发框架,几乎所有需要异步或者并发执行任务的程序都可以使用线程池。
使用线程池的有三个优点:
- 降低资源的消耗:通过重复利用已创建的线程降低线程创建和销毁带来来的消耗
- 提高响应速度:当新任务到达时,任务可以不需要等待线程创建就可以立即执行
- 提高线程的可管理性:使用线程池可以统一进行线程分配、调度与监控
线程池的实现原理
当一个Runnable或Callable对象到达线程池时,执行策略如下:
- 首先判断核心线程池中的线性是否都在执行任务,如果是,再次查看核心线程池是否已满,如果未满,创建新的线程执行任务。如果核心线程池有空闲线程,则将任务直接分配给空闲线程执行。否则执行第二步。
- 判断工作队列(BlockingQueue)是否已满,如果工作队列没有满,将提交任务存储到工作队列中等待核心池的的调度;否则,若工作队列已满,进入步骤3。
- 判断线程池中的线程数是否已达到最大值maxiumSize,若已达到最大值,将任务交给饱和策略处理;否则,继续创建新线程执行此任务。
线程池的创建
通过创建ThreadPoolExecutor()
来创建线程池:
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
-
corePoolSize(核心池的大小):当提交一个任务到线程池时,线程池会创建一个新的线程来执行任务。即使核心池中有其他空闲线程能够执行新任务也会创建新线程,一直到线程数达到了核心池的大小为止。
如果调用prestartAllCoreThreads()
方法,线程池会提前创建并启动所有核心线程。
/**
* Starts all core threads, causing them to idly wait for work. This
* overrides the default policy of starting core threads only when
* new tasks are executed.
*
* @return the number of threads started
*/
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
- workQueue(工作队列):用于保存等待执行任务的阻塞队列。可以选择以下几个阻塞队列:
- ArrayBlockingQueue:基与数组结构的有界阻塞队列,此队列按照FIFO原则对元素进行排序。
- LickedBlockingQueue:基于链表结构的阻塞队列,按照FIFO排序元素,吞吐量高于ArrayBlockingQueue,静态工厂方法
Executor.newFixedThreadPool()
采用此队列。 - synchronousQueue:一个不存储元素的阻塞队列,无界队列。每个插入操作必须等待另一个线程调用移除操作,否则插入操作一直处于阻塞状态,通常吞吐量比LickedBlockingQueue还要高,静态工厂方法
Executor.newCachedThreadPool()
采用此队列。 - PriorityBlockingQueue:具有优先级的无界阻塞队列。
-
maximumPoolSize(线程池最大线程数量):线程池允许创建的最大线程数。如果队列已满并且已创建的线程数小于此参数,则线程池会再创建新的线程执行任务;否则,调用饱和策略处理。如果采用无界队列,此参数无意义。
-
keepAliveTime(线程保持活动的时间):线程池的工作线程空闲后,保持存活的时间。若任务很多,并且每个任务执行的时间较短,可以调大此参数来提高线程利用率。
-
TimeUnit(线程保持活动的时间的时间单位):可选的单位有天(DAYS)、小时(HOURS)、分钟 ( MINUTES ) 、 毫 秒 ( MILLISECONDS ) 、 微 秒 ( MICROSECONDS , 千 分 之 一 毫 秒 ) 和 纳 秒 (NANOSECONDS,千分之一微秒)。
-
RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态。此时采用饱和策略来处理任务,默认采用AbortPolicy,,表示无法处理新任务时抛出异常。JDK一共内置四种饱和策略:
- AbortPolicy,表示无法处理新任务抛出异常,JDK默认采用此策略
- CallerRunsPolicy,等到调用者线程空闲后运行任务
- DiscardOldestPolicy,丢弃阻塞队列中最近的一个任务,并执行当前任务
- DiscardPolicy:不处理,直接将新任务丢弃掉,也不报错
创建一个线程池
可以使用两个方法向线程池提交任务,分别为execute()和submit()方法。
采用execute()
方法
用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService =
new ThreadPoolExecutor(3,5,
2000, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>());
for (int i = 0;i < 5;i++){
executorService.execute(()->{
for (int j = 0;j < 10;j++){
System.out.println(Thread.currentThread().getName()+"、"+j);
}
});
}
executorService.shutdown();
}
}
采用submit()方法
submit()方法用于提交需要返回值的任务。
线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
import java.util.concurrent.*;
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService =
new ThreadPoolExecutor(3,5,
2000, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>());
for (int i = 0;i < 5;i++){
java.util.concurrent.Future future = executorService.submit(()->{
for (int j = 0;j < 10;j++){
System.out.println(Thread.currentThread().getName()+"、"+j);
}
return Thread.currentThread().getName()+"执行结束";
});
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executorService.shutdown();
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.ThreadPool
pool-1-thread-1、0
pool-1-thread-1、1
pool-1-thread-1、2
pool-1-thread-1、3
pool-1-thread-1、4
pool-1-thread-1、5
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-1、8
pool-1-thread-1、9
pool-1-thread-1执行结束
pool-1-thread-2、0
pool-1-thread-2、1
pool-1-thread-2、2
pool-1-thread-2、3
pool-1-thread-2、4
pool-1-thread-2、5
pool-1-thread-2、6
pool-1-thread-2、7
pool-1-thread-2、8
pool-1-thread-2、9
pool-1-thread-2执行结束
pool-1-thread-3、0
pool-1-thread-3、1
pool-1-thread-3、2
pool-1-thread-3、3
pool-1-thread-3、4
pool-1-thread-3、5
pool-1-thread-3、6
pool-1-thread-3、7
pool-1-thread-3、8
pool-1-thread-3、9
pool-1-thread-3执行结束
pool-1-thread-1、0
pool-1-thread-1、1
pool-1-thread-1、2
pool-1-thread-1、3
pool-1-thread-1、4
pool-1-thread-1、5
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-1、8
pool-1-thread-1、9
pool-1-thread-1执行结束
pool-1-thread-2、0
pool-1-thread-2、1
pool-1-thread-2、2
pool-1-thread-2、3
pool-1-thread-2、4
pool-1-thread-2、5
pool-1-thread-2、6
pool-1-thread-2、7
pool-1-thread-2、8
pool-1-thread-2、9
pool-1-thread-2执行结束
FutureTask类执行任务只执行一次,并且会阻塞其他线程。Future.get()会阻塞其他线程,一直等到当前Callable线程执行完毕拿到返回值为止。
关闭线程池
可以通过调用线程池的shutdown
或shutdownNow
方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt
方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别。shutdownNow
首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。shutdown
只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
只要调用了这两个关闭方法中的任意一个,isShutdown
方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed
方法会返回true。至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown
方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow
方法。
JDK内置四大线程池
Executor框架核心的类是ThreadPoolExecutor
,它是线程池的实现类。通过Executor框架的工具类Executors
,可以 创建两种类型的线程池。
普通调度池
创建无大小限制的线程池:Executors.newCachedThreadPool()
适用于很多短期任务的小程序,负载较轻的服务器。
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CachedThreadPool
的corePoolSize
被设置为0,即corePool
为空;maximumPoolSize
被设置为Integer.MAX_VALUE
,即 maximumPool
是无界的。这里把keepAliveTime
设置为60L,意味着CachedThreadPool
中的空闲线程等待新任务的长时间为60秒,空闲线程超过60秒后将会被终止。
FixedThreadPool
和 SingleThreadExecutor
使 用 无 界 队 列 LinkedBlockingQueue
作 为 线 程 池 的 工 作 队 列 。CachedThreadPool
使用没有容量的SynchronousQueue
作为线程池的工作队列,但CachedThreadPool
的maximumPool
是无界的。这意味着,如果主线程提交任务的速度高于maximumPool
中线程处理任务的速度时,CachedThreadPool
会不断创建新线程。极端情况下,CachedThreadPool
会因为创建过多线程而耗尽CPU和内存资源。
CachedThreadPool的execute()方法的执行示意图如下图所示:
// 使用缓冲线程池
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService =
Executors.newCachedThreadPool();
for (int i = 0;i < 5;i++){
executorService.submit(()->{
for (int j = 0;j < 10;j++){
System.out.println(Thread.currentThread().getName()+"、"+j);
}
});
}
executorService.shutdown();
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.ThreadPool
pool-1-thread-2、0
pool-1-thread-2、1
pool-1-thread-2、2
pool-1-thread-2、3
pool-1-thread-5、0
pool-1-thread-4、0
pool-1-thread-1、0
pool-1-thread-1、1
pool-1-thread-3、0
pool-1-thread-3、1
pool-1-thread-1、2
pool-1-thread-4、1
pool-1-thread-5、1
pool-1-thread-2、4
pool-1-thread-5、2
pool-1-thread-4、2
pool-1-thread-1、3
pool-1-thread-3、2
pool-1-thread-1、4
pool-1-thread-4、3
pool-1-thread-4、4
pool-1-thread-5、3
pool-1-thread-2、5
pool-1-thread-2、6
pool-1-thread-2、7
pool-1-thread-2、8
pool-1-thread-2、9
pool-1-thread-5、4
pool-1-thread-5、5
pool-1-thread-5、6
pool-1-thread-5、7
pool-1-thread-5、8
pool-1-thread-5、9
pool-1-thread-4、5
pool-1-thread-4、6
pool-1-thread-4、7
pool-1-thread-4、8
pool-1-thread-4、9
pool-1-thread-1、5
pool-1-thread-3、3
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-3、4
pool-1-thread-1、8
pool-1-thread-3、5
pool-1-thread-1、9
pool-1-thread-3、6
pool-1-thread-3、7
pool-1-thread-3、8
pool-1-thread-3、9
Process finished with exit code 0
创建固定大小的线程池: Executors.newFixedThreadPool(int nThread)
适用于为了满足资源管理的需求而需要限制当前线程数量的应用场景,适用于负载比较重的服务器。
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool
使用无界队列 LinkedBlockingQueue
作为线程池的工作队列( 列 的 容 量 为 Integer.MAX_VALUE)。使用无界队列作为工作队列会对线程池带来如下影响:
1)当线程池中的线程数达到corePoolSize
后,新任务将在无界队列中等待,因此线程池中的线程数不会超过 corePoolSize
。
2)由于1),使用无界队列时maximumPoolSize
将是一个无效参数。
3)由于1)和2),使用无界队列 时keepAliveTime
将是一个无效参数。
4)由于使用无界队列,运行中的FixedThreadPool
(未执行方法 shutdown()
或 shutdownNow()
)不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution
方法)
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService =
Executors.newFixedThreadPool(3);
for (int i = 0;i < 5;i++){
executorService.submit(()->{
for (int j = 0;j < 10;j++){
System.out.println(Thread.currentThread().getName()+"、"+j);
}
});
}
executorService.shutdown();
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.ThreadPool
pool-1-thread-2、0
pool-1-thread-3、0
pool-1-thread-3、1
pool-1-thread-3、2
pool-1-thread-3、3
pool-1-thread-1、0
pool-1-thread-3、4
pool-1-thread-2、1
pool-1-thread-2、2
pool-1-thread-2、3
pool-1-thread-3、5
pool-1-thread-3、6
pool-1-thread-3、7
pool-1-thread-3、8
pool-1-thread-3、9
pool-1-thread-1、1
pool-1-thread-3、0
pool-1-thread-3、1
pool-1-thread-3、2
pool-1-thread-3、3
pool-1-thread-3、4
pool-1-thread-3、5
pool-1-thread-3、6
pool-1-thread-3、7
pool-1-thread-3、8
pool-1-thread-3、9
pool-1-thread-2、4
pool-1-thread-3、0
pool-1-thread-3、1
pool-1-thread-3、2
pool-1-thread-3、3
pool-1-thread-3、4
pool-1-thread-3、5
pool-1-thread-3、6
pool-1-thread-3、7
pool-1-thread-1、2
pool-1-thread-3、8
pool-1-thread-3、9
pool-1-thread-2、5
pool-1-thread-2、6
pool-1-thread-2、7
pool-1-thread-2、8
pool-1-thread-2、9
pool-1-thread-1、3
pool-1-thread-1、4
pool-1-thread-1、5
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-1、8
pool-1-thread-1、9
Process finished with exit code 0
单线程池:Executors. newSingleThreadExecutor()
适用于需要保证顺序的执行各个任务,并且在任意时间点,不会有多个线程活动的场景。
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newSingleThreadExecutor
的corePoolSize
和maximumPoolSize
被设置为1 。其他参数与FixedThreadPool
相同 。 SingleThreadExecutor
使 用 无 界 队 列 LinkedBlockingQueue
作 为 线 程 池 的 工 作 队 列 ( 队 列 的 容 量 为 Integer.MAX_VALUE)。
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService =
Executors.newSingleThreadExecutor();
for (int i = 0;i < 5;i++){
executorService.submit(()->{
for (int j = 0;j < 10;j++){
System.out.println(Thread.currentThread().getName()+"、"+j);
}
});
}
executorService.shutdown();
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.ThreadPool
pool-1-thread-1、0
pool-1-thread-1、1
pool-1-thread-1、2
pool-1-thread-1、3
pool-1-thread-1、4
pool-1-thread-1、5
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-1、8
pool-1-thread-1、9
pool-1-thread-1、0
pool-1-thread-1、1
pool-1-thread-1、2
pool-1-thread-1、3
pool-1-thread-1、4
pool-1-thread-1、5
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-1、8
pool-1-thread-1、9
pool-1-thread-1、0
pool-1-thread-1、1
pool-1-thread-1、2
pool-1-thread-1、3
pool-1-thread-1、4
pool-1-thread-1、5
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-1、8
pool-1-thread-1、9
pool-1-thread-1、0
pool-1-thread-1、1
pool-1-thread-1、2
pool-1-thread-1、3
pool-1-thread-1、4
pool-1-thread-1、5
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-1、8
pool-1-thread-1、9
pool-1-thread-1、0
pool-1-thread-1、1
pool-1-thread-1、2
pool-1-thread-1、3
pool-1-thread-1、4
pool-1-thread-1、5
pool-1-thread-1、6
pool-1-thread-1、7
pool-1-thread-1、8
pool-1-thread-1、9
Process finished with exit code 0
定时调度池
ScheduledThreadPoolExecutor
继承自ThreadPoolExecutor
。它主要用来在给定的延迟之后运行任务,或者定期执行任 务。ScheduledThreadPoolExecutor
的功能与Timer类似,但ScheduledThreadPoolExecutor
功能更强大、更灵活。Timer 对应的是单个后台线程,而ScheduledThreadPoolExecutor
可以在构造函数中指定多个对应的后台线程数。
DelayQueue
是一个无界队列,所以ThreadPoolExecutor
的maximumPoolSize
在ScheduledThreadPoolExecutor
中没有什 么意义(设置maximumPoolSize
的大小没有什么效果)。ScheduledThreadPoolExecutor
的执行主要分为两大部分:
1)当调用ScheduledThreadPoolExecutor
的scheduleAtFixedRate()
方法或者scheduleWithFixedDelay()
方法时,会向 ScheduledThreadPoolExecutor
的 DelayQueue
添加一个实现了 RunnableScheduledFutur
接口的ScheduledFutureTask
。
2)线程池中的线程从DelayQueue
中获取ScheduledFutureTask
,然后执行任务。
ScheduledThreadPoolExecutor
为了实现周期性的执行任务,对ThreadPoolExecutor
做了如下的修改:
- 使用
DelayQueue
作为任务队列。 - 获取任务的方式不同执行周期任务后。
- 增加了额外的处理。
// 使用定时调度池
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService =
Executors.newScheduledThreadPool(3);
for (int i = 0;i < 5;i++){
((ScheduledExecutorService) executorService).scheduleAtFixedRate(()->{
for (int j = 0;j < 10;j++){
System.out.println(Thread.currentThread().getName()+"、"+j);
}
},2,3,TimeUnit.SECONDS);
}
}
}
Executor框架
在Java中,使用线程来异步执行任务。Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源。同时,为每一个任务创建一个新线程来执行,这种策略可能会使处于高负荷状态的应用终崩溃。
Java的线程既是工作单元,也是执行机制。从JDK 5开始,把工作单元与执行机制分离开来。工作单元包括Runnable 和 Callable,而执行机制由Executor框架提供。
Executor框架的两级调度模型
Java线程(java.lang.Thread)被一对一映射为本地操作系统线程。Java线程启动时会创建一个本地操作系统线程; 当该Java线程终止时,这个操作系统线程也会被回收。操作系统会调度所有线程并将它们分配给可用的CPU。
在上层,Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。这种两级调度模型的示意图如下图所示:
Executor框架的结构与成员
合理配置线程池
要想合理地配置线程池,就必须首先分析任务特性,可以从以下几个角度来分析。
- 任务的性质:CPU密集型任务、IO密集型任务和混合型任务。
- 任务的优先级:高、中和低。
- 任务的执行时间:长、中和短。
- 任务的依赖性:是否依赖其他系统资源,如数据库连接。
性质不同的任务可以用不同规模的线程池分开处理。
CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。混合型的任务, 如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大, 那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。
可以通过Runtime.getRuntime().availableProcessors()
方法获得当前设备的CPU个数。
优先级不同的任务可以使用优先级队列PriorityBlockingQueue
来处理。它可以让优先级高的任务先执行。
注意:如果一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行。
执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让执行时间短的任务先执行。
依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU。