Java并发编程实战

来源:互联网 发布:电熨斗推荐 知乎 编辑:程序博客网 时间:2024/05/01 19:50

如何配置和调优线程池?
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();        }    }}
0 0
原创粉丝点击