Java并发编程实战 - 第8章 线程池的使用

如何配置和调优线程池?
Executor 将任务的提交与任务的执行策略解耦开来。

不同的任务有不同的执行策略,以此考虑线程池的大小或配置限制

  • 独立的任务
  • 依赖性的任务
    • 在线程池中,如果一个任务等待其他任务的结果,可能出现线程饥饿死锁。
  • 使用线程封闭机制的任务
    • 单线程的Executor
  • 运行时间较长的任务
    • 任务阻塞时间过长。可以通过限制任务等待资源的时间。如限时版的Thread.join, BlockingQueue.put等
  • 使用Threadlocal的任务
  • 同类型的相互独立的任务

设置线程池大小
线程池过大,大量的线程在相当较少的处理器和内存资源上发生竞争,导致更高的内存使用率,可能耗尽资源;
线程池过小,可能导致许多空闲的处理器无法执行工作,从而降低吞吐量。

线程池大小受资源和任务特性影响, 资源如CPU,内存,文件句柄,套接字句柄,数据库连接池等。任务是计算密集型或IO密集型。

公式: N(threads) = N(CPU) * U(CPU) * (1 + W/C)
理想的线程池大小=CPU数量 * CPU的使用率 *(1+任务的等待时间/任务的计算时间)。
可以通过Runtime获取CPU的数目:
Runtime.getRuntime().availableProcessors();

配置ThreadPoolExecutor
可以通过ThreadPoolExecutor构造函数配置线程池:

public ThreadPoolExecutor (
    int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workqueue,
    ThreadFactory threadFactory,
    RejectedExecutorHandler handler)

参数详解:
corePoolSize: 基本大小,只有在工作队列满了的情况下创建超出这个数量的线程。
maximumPoolSize:最大大小,同时活动的线程数量上限。
keepAliveTime:存活时间,如果某个线程的空闲时间超过了存活时间,线程将被标记为可回收,当线程池的大小超过基本大小,这个线程将被终止。
workqueue: 保存等待执行的任务
任务的排队方法有3中:无界队列,有界队列和同步移交。
队列的选择和其他参数相关。
newFixedThreadPool & newSingleThreadPool 默认使用无界队列 LinkedBlockingQueue;
newCachedThreadPool使用SynchronousQueue;
如果非常大或无界的线程池,可以同过SynchronousQueue来避免排队。
如果任务相当独立,为线程池或队列设置界限才合理。
如果任务之间依赖,应使用无界线程池如newCachedThreadPool。
使用有界队列的时候有助于避免资源耗尽的情况,队列的大小和线程池的大小要一起调节。
handler:饱和策略
使用有界队列的时候,队列满时,任务如何处理?
四种饱和策略:
AbortPolicy(抛出异常), CallerRunsPolicy(任务退回到调用者), DiscardPolicy(抛弃该任务), DiscardOldestPolicy(抛弃最旧的任务)。
threadFactory:线程工厂
每当线程池创建线程时,都通过ThreadFactory的工厂方法newThread创建。可以定制ThreadFactory。

扩展ThreadPoolExecutor
ThreadPoolExecutor提供了在子类化中可以override的方法
beforeExecutor, afterExecutor, terminated, 这些方法可以用来添加日志,计时,监视或统计信息等功能。

示例:给线程池添加统计信息

public class TimingThreadPool extends ThreadPoolExecutor {

    public TimingThreadPool() {
        super(1, 1, 0L, TimeUnit.SECONDS, null);
    }

    private final ThreadLocal<Long> startTime = new ThreadLocal<Long>();
    private final Logger log = Logger.getLogger("TimingThreadPool");
    private final AtomicLong numTasks = new AtomicLong();
    private final AtomicLong totalTime = new AtomicLong();

    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        log.fine(String.format("Thread %s: start %s", t, r));
        startTime.set(System.nanoTime());
    }

    protected void afterExecute(Runnable r, Throwable t) {
        try {
            long endTime = System.nanoTime();
            long taskTime = endTime - startTime.get();
            numTasks.incrementAndGet();
            totalTime.addAndGet(taskTime);
            log.fine(String.format("Thread %s: end %s, time=%dns",
                    t, r, taskTime));
        } finally {
            super.afterExecute(r, t);
        }
    }

    protected void terminated() {
        try {
            log.info(String.format("Terminated: avg time=%dns",
                    totalTime.get() / numTasks.get()));
        } finally {
            super.terminated();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值