使用Java构造高可扩展应用 - 编程入门网
老式日志服务器,而绿色的曲线 是我们进行了性能调优之后的日志服务器。可以看到,LogServerBad 的性能随 线程数目的增加变化很小,而 LogServerGood 的性能则随着线程数目的增加而 线性增长。如果不介意使用第三方的库的话,那么来自 Project KunMing 的 LockFreeQueue 可以进一步提供更好的可扩展性:
图 3. 使用 Lock-free 的数据结构 在上图中,第三条曲线表示用 LockFreeQueue 替换标准库中的 ConcurrentLinkedQueue 之后的性能曲线。可以看到,如果线程数目较少时,两 条曲线差别不大,但是单线程数目增大到一定程度之后,Lock-Free 的数据结构 具有明显的优势。 在下文中,将介绍在上述例子中使用的可以帮助我们创建高可扩展 Java 应 用的工具和技巧。 使用Java构造高可扩展应用(3)时间:2011-06-16 IBM / 戴 晓君 甘 志 齐 尧 罗 志达使用 JLM 分析应用程序 JLM 提供了 Java 应用和 JVM 中锁持有时间和冲突统计。具体提供以下功能 : 对冲突的锁进行计数 成功获得锁的次数 递归锁的次数 申请锁的线程被阻塞等待的次数 锁被持有的累计时间。对于支持 3 Tier Spin Locking 的平台 , 还可以获 得以下信息 : 请求线程在内层(spin loop)请求锁的次数 请求线程在外层(thread yield loop)请求锁的次数 使用 rtdriver 工具收集更详细的信息 jlmlitestart:仅收集计数器 jlmstart:仅收集计数器和持有时间统计 jlmstop:停止数据收集 jlmdump:打印数据收集并继续收集过程 从锁持有时间中去除垃圾收集(Garbage Collection,GC)的时间 GC 时间从 GC 周期中所有被持有的锁的持有时间中去除 使用 AtomicInteger 进行计数 通常,在我们实现多线程使用的计数器或随机数生成器时,会使用锁来保护 共享变量。这样做的弊端是如果锁竞争的太厉害,会损害吞吐量,因为竞争的同 步非常昂贵。 volatile 变量虽然可以使用比同步更低的成本存储共享变量,但它只可以保 证其他线程能够立即看到对 volatile 变量的写入,无法保证读 - 修改 - 写的 原子性。因此,volatile 变量无法用来实现正确的计数器和随机数生成器。 从 JDK 5 开始,java.util.concurrent.atomic 包中引入了原子变量,包括 AtomicInteger、AtomicLong、AtomicBoolean 以及数组 AtomicIntergerArray 、AtomicLongArray 。原子变量保证了 ++,--,+=,-= 等操作的原子性。利用 这些数据结构,您可以实现更高效的计数器和随机数生成器。 加入轻量级的线程池—— Executor 大多数并发应用程序是以执行任务(task)为基本单位进行管理的。通常情 况下,我们会为每个任务单独创建一个线程来执行。这样会带来两个问题:一, 大量的线程(>100)会消耗系统资源,使线程调度的开销变大,引起性能下 降;二,对于生命周期短暂的任务,频繁地创建和消亡线程并不是明智的选择。 因为创建和消亡线程的开销可能会大于使用多线程带来的性能好处。 一种更加合理的使用多线程的方法是使用线程池(Thread Pool)。 java.util.concurrent 提供了一个灵活的线程池实现:Executor 框架。这个框 架可以用于异步任务执行,而且支持很多不同类型的任务执行策略。它还为任务 提交和任务执行之间的解耦提供了标准的方法,为使用 Runnable 描述任务提供 了通用的方式。 Executor 的实现还提供了对生命周期的支持和 hook 函数,可 以添加如统计收集、应用程序管理机制和监视器等扩展。 在线程池中执行任务线程,可以重用已存在的线程,免除创建新的线程。这 样可以在处理多个任务时减少线程创建、消亡的开销。同时,在任务到达时,工 作线程通常已经存在,用于创建线程的等待时间不会延迟任务的执行,因此提高 了响应性。通过适当的调整线程池的大小,在得到足够多的 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |