Java 中创建一个线程往往意味着JVM 会创建相应依赖于宿主机操作系统的本地线程(Native Thread)。
Thread Pool 模式的本质是使用极其有限的资源去处理相对无限的任务。核心思想是使用队列对待处理的任务进行缓存,并复用一定数量的工作者线程去取队列中的任务进行执行。
工作队列通常可以在有界队列(Bounded Queue)、无界队列(Unbound Queue) 和直接交接队列(SynchronousQueue) 之间选择。因此,无界工作队列可能会导致系统的不稳定性,适合在任务占用的内存空间以及其他稀缺资源比较少的情况下使用。
9.4.2 线程池大小的调校
任务的特性主要考虑任务是CPU 密集型、I/O密集型,还是混合型。
- 对于CPU 密集型任务,相应的线程池的大小可以考虑设置为 Ncpu+1 N c p u + 1 。+1 考虑到即便是CPU 密集型的任务,其执行线程也可能在某一个时刻(如因为缺页中断),而出现等待。
对于I/O 密集型任务,我们需要注意I/O 操作会引起上下文切换。
在操作系统中,CPU切换到另一个进程需要保存当前进程的状态并恢复另一个进程的状态:当前运行任务转为就绪(或者挂起、删除)状态,另一个被选定的就绪任务成为当前任务。上下文切换包括保存当前任务的运行环境,恢复将要运行任务的运行环境。
进程上下文用进程的PCB(进程控制块,也称为PCB,即任务控制块)表示,它包括进程状态,CPU寄存器的值等。
不妨将相应的线程池大小设为 2∗Ncpu 2 ∗ N c p upublic static void main(String[] args){ ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, Runtime.getRuntime().availableProcessors()*2, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(200)); threadPool.submit(new IOIntensiveTask()); } private static class IOIntensiveTask implements Runnable{ public void run() { System.out.println("lele"); } }
对于混合型,可以考虑将其拆分成CPU密集型和I/O密集型 的子任务。
一个计算线程合理大小的公式,为:
S+Ncpu∗Ucpu∗(1+WTST) S + N c p u ∗ U c p u ∗ ( 1 + W T S T )
S S 为线程池合理大小, Ncpu N c p u 为CPU 个数, Ucpu U c p u 为目标CPU 使用率, WT W T 为任务执行线程进行等待的时间, ST S T 为任务执行线程使用CPU 进行计算的时间。 ST、WT S T 、 W T 可以借助工具(如 jvisualvm)计算出相应值。
9.4.3 线程池监控
9.4.4 线程泄漏
9.4.5 可靠性与线程池饱和处理策略
9.4.6 防止死锁
public class ThreadPoolDeadLockAvoidance {
private final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args){
ThreadPoolDeadLockAvoidance me = new ThreadPoolDeadLockAvoidance();
me.test("This will not deadlock");
}
public void test(final String message){
Runnable taskA = new Runnable() {
public void run() {
System.out.println("Executing TaskA ...");
Runnable taskB = new Runnable() {
public void run() {
System.out.println("TaskB processes "+ message);
}
};
Future<?> result = threadPool.submit(taskB);
try{
result.get();
}catch (InterruptedException e){
;
}catch (ExecutionException e){
e.printStackTrace();
}
System.out.println("Task A Done.");
}
};
threadPool.submit(taskA);
}
}