Posted on 2005-12-12 09:05
笨笨 阅读(1818)
评论(2) 编辑 收藏 所属分类:
Java
减少全局竞争性同步,提高应用的垂直扩展能力
垂直扩展,简单的说,是当单一系统硬件升级扩展时,如增加CPU,内存,应用程序能够随之线性提高业务处理能力。
多线程是服务端 Java 应用的标准处理方式,其优点不用赘述。本文要讨论的是,如何在设计阶段降低多线程之间的竞争性同步开销。
假设一个Web应用,需要为当前用户维护在线用户信息。此用户信息列表会放在 Application 范围的一个 Map 中,那么我们增加或删除一个在线用户的操作会是这样:。
Map clientMap = ...// from Application Context
synchronized(clientMap){
clientMap.put(clientId,clientObject);
}
这是一个典型的全局同步代码,当并发线程增加时,这部分代码就有可能会存在潜在垂直扩展瓶颈。
最简单解决办法:用 ConcurrentHashMap。
ConcurrentHashMap的多线程下的表现要比HashMap好的多,可以做到随着线程数增长性能基本保持稳定。
参见:http://www-128.ibm.com/developerworks/cn/java/j-jtp07233/index.html
对 ConcurrentHashMap 的分析,参见:http://www-128.ibm.com/developerworks/cn/java/j-jtp08223/index.html
在 ConcurrentMap/ConcurrentLinkedQueue 不能帮助我们的情况下,我们需要明确设计以避免全局竞争。
基本原则是:
1 预分配,降低争用出现的频率。
2 降低锁的粒度,将全局竞争变为局部竞争。
预分配策略示例:
对于一在线交易处理系统,需要为每个交易生成交易流水号,假设有多台交易服务器按照集群方式配置,同时提供服务。那么需要在交易服务器之间进行同步,以保证交易流水号的正常增长。
一种处理方式为:在数据库中保存当前交易流水号的最高值,每台机器一次预分配1000流水,内部采用线程同步进行分配,用完再从数据库分配。这里数据库充当了全局存储和全局同步工具,如果每来一条交易,就访问一次数据库,考虑到数据库同步和事务的负担,这里会成为严重的性能瓶颈。
降低锁粒度策略示例:
1 ConcurrentHashMap 本身就是个很好的模范。它采用32颗锁,来代替普通 HashMap 的单颗对象锁。
2 对于数据库中并发大的表,可以考虑将表级锁改为行级锁,提高并发性。
暂时想不起来示例,有空再补