一种常见的并发编程场景的处理
对于并发编程,大家想到总是多线程之间对等的临界资源竞争。然而经常会遇到下面这样的场景:
守护线程提供一个临界资源,多个子线程会并发改写该临界资源。大部分时候(99.9%的时间),主线程是不会干涉各个线程之间的竞争的,通常只要该临界资源自己内部处理好同步即可。但是偶尔主线程也会干预一下该临界资源,比如做一些统计,做一个快照,或者复制数据然后清空等。这个操作通常会耗时比较长,并且在此期间不希望有人改写临界资源。如果,主线程与各个子线程使用同样的锁或者synchronized同步,那么在主线程没有作该操作时,各个子线程之间会因为竞争而阻塞,这个阻塞开起来是没有必要的。
这里介绍一个利用volatile变量的特性解决该问题。试图在性能和数据保护上面达到最大平衡。Atomic变量采用的是寄存器变量实现的。用两个变量标志map表当前的状态。而不必在后台线程和众多业务线程之间加锁或者同步,由于在多数情况下volatile变量的性能优于锁(Java 理论与实践: 正确使用 Volatile 变量)。这里只以map表举例,其他保证线程安全的数据结构也适用。
试想,在addOneRecord方法和getAndClearAllRecords方法之间引入一个锁或者synchronized,那么每两个业务线程在添加数据的时候都要竞争,而这个竞争是不必要的。因为,只有在后台线程调用getAndClearAllRecords时,这个锁才有意义。这里引入两个寄存器变量,能有效降低一些锁竞争的问题。当然这里只是拿concurrenthashmap来举一个例子,也许在doSomeThingWithMap方法中根本没有什么需要独占整个map表的,那么整个同步机制都不需要了,只要map表自身的同步即可。
/*** 用于标志长时间独占表的线程是否正在工作。
*/
private AtomicBoolean closed = new AtomicBoolean(false);
/**
* 用于标志当前的数据表是否正在插入数据。
*/
private AtomicInteger busy = new AtomicInteger();
/**
* 用于存储数据的表格。
*/
private ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
/**
* 多个线程会并发调用该接口向
* map表中插入数据。
* @param key
* @param value
*/
public void addOneRecord(String key, String value)
{
/**
* 大多数时候,该循环都不会占用性能资源。
*/
while (closed.get())
{
waitAMoment();
}
if (map != null)
{
/**
* 用一个同步变量在各个线程中计数。
*/
busy.getAndIncrement();
map.put(key, value);
busy.getAndDecrement();
}
}
/**
* 后台线程会定期处理map表中的数据。
*/
public void getAndClearAllRecords()
{
synchronized (this)
{
/**
* 先将closed标志设置,以便独占该map表;
* 避免操作过程中被修改。
*/
closed.set(true);
while (busy.get() != 0)
{
waitAMoment();
}
doSomeThingWithMap(map);
closed.set(false);
}
}
private void doSomeThingWithMap(ConcurrentHashMap<String, String> map)
{
// TODO Auto-generated method stub
}
private void waitAMoment()
{
// TODO Auto-generated method stub
}
建议继续学习:
- Rolling cURL: PHP并发最佳实践 (阅读:10355)
- 查看 Apache并发请求数及其TCP连接状态 (阅读:8494)
- 大型高并发高负载网站的系统架构分析 (阅读:7735)
- 大并发下的高性能编程 – 改进的(用户态)自旋锁 (阅读:7172)
- 并发编程系列之一:锁的意义 (阅读:5914)
- 并发框架Disruptor译文 (阅读:5126)
- 学习:一个并发的Cache (阅读:4992)
- C++多进程并发框架 (阅读:4769)
- PHP 持久连接于并发 (阅读:4337)
- Squid 限制用户并发连接数 (阅读:3959)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:童燕群 来源: 忘我的追寻
- 标签: 并发 并发编程
- 发布时间:2013-10-29 12:23:14
- [51] Oracle MTS模式下 进程地址与会话信
- [49] 图书馆的世界纪录
- [49] IOS安全–浅谈关于IOS加固的几种方法
- [49] 如何拿下简短的域名
- [45] android 开发入门
- [44] 【社会化设计】自我(self)部分――欢迎区
- [42] 界面设计速成
- [42] 读书笔记-壹百度:百度十年千倍的29条法则
- [41] 视觉调整-设计师 vs. 逻辑
- [40] Go Reflect 性能