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(); } }}
- Java并发编程实战
- Java并发编程实战--
- Java并发编程实战-
- Java 并发编程实战
- java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- Java并发编程实战
- MyEclipse+Tomcat+MAVEN+SVN项目完整环境搭建
- QT关于qml的Component事件
- 有用的网站
- Centos7下的安装Docker1.8
- 【持续更新】[2017-4-26]红米NOTE3双网全网-信号居左-时间居中-IOS状态栏-集合贴
- Java并发编程实战
- 实现Http Server的三种方法
- Spring dataSource再探
- HDFS简介
- Less-11&12
- D题 走迷宫(dfs+二分枚举)
- Java内存区域划分、内存分配原理
- signal 进程通信
- Modal模态框的防穿透问题,模态框弹出后,禁止body滑动事件