mkr*_*hin 10 java concurrency multithreading volatile concurrenthashmap
简介
假设我有一个ConcurrentHashMap单例:
public class RecordsMapSingleton {
private static final ConcurrentHashMap<String,Record> payments = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, Record> getInstance() {
return payments;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我有来自不同来源的三个后续请求(全部由不同线程处理).
第一个服务发出请求,获取单例,创建Record实例,生成唯一ID并将其放入Map,然后将此ID发送到另一个服务.
然后第二个服务使用该ID发出另一个请求.它获取单例,查找Record实例并修改它.
最后(可能在半小时后)第二个服务发出另一个请求,以便Record进一步修改.
问题
在一些非常罕见的情况下,我遇到了 heisenbug.在日志中我可以看到,第一个请求成功放置Record到Map,第二个请求发现它的ID,并修改它,然后第三个请求,试图找到ID记录,但一无所获(get()返回null).
我发现有关ConcurrentHashMap担保的一件事是:
在将对象放入任何并发集合之前的线程中的操作发生在从另一个线程中的集合访问或移除该元素之后的操作之前.
从这里开始.如果我做对了,它的字面意思是,它get()可以返回实际上某个时间到Map的任何值,只要它不会破坏happens-before不同线程中的动作之间的关系.
在我的情况下,它适用于这样:如果第三个请求不关心在处理第一个和第二个期间发生的事情,那么它可以null从中读取Map.
它不适合我,因为我真的需要从Map最新的实际中得到Record.
我尝试过什么
所以我开始思考,如何形成happens-before后续Map修改之间的关系; 并有想法.JLS 说(在17.4.4)认为:
对易失性变量v(第8.3.1.4节)的写入与任何线程对v的所有后续读取同步(其中"后续"根据同步顺序定义).
所以,我们假设,我会像这样修改我的单身:
public class RecordsMapSingleton {
private static final ConcurrentHashMap<String,Record> payments = new ConcurrentHashMap<>();
private static volatile long revision = 0;
public static ConcurrentHashMap<String, Record> getInstance() {
return payments;
}
public static void incrementRevision() {
revision++;
}
public static long getRevision() {
return revision;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在每次修改Map或Record内部之后,我会打电话incrementRevision(),在任何地图阅读之前我都会打电话getRevision().
问题
由于heisenbugs的性质,没有多少测试足以证明这个解决方案是正确的.而且我不是并发专家,所以无法正式验证它.
有人可以批准,遵循这种方法可以保证我总能获得最新的实际价值ConcurrentHashMap吗?如果这种方法不正确或效率低下,你能推荐一些别的东西吗?
你的方法不会起作用,因为你实际上又重复了同样的错误.作为ConcurrentHashMap.put并将在关系之前ConcurrentHashMap.get创建一个发生但没有时间命令保证,完全相同的情况适用于您对volatile变量的读取和写入.它们在关系之前形成一个发生但没有时间命令保证,如果一个线程get在另一个执行之前调用put,则同样适用于volatile在volatile写入之前将发生的读取.除此之外,您正在添加另一个错误,因为将++操作符应用于volatile变量不是原子的.
对volatile变量的保证并不比为a做的更强ConcurrentHashMap.它的文档明确指出:
检索反映了最近完成的更新操作的结果.
线程间操作是由一个线程执行的操作,可以被另一个线程检测到或直接受其影响.程序可以执行几种类型的线程间操作:
...
- 外部行动.外部动作是可以在执行之外观察的动作,并且具有基于执行外部的环境的结果.
简单地说,如果一个线程放入a ConcurrentHashMap并向外部实体发送消息,并且第二个线程在从ConcurrentHashMap外部实体接收到消息后根据先前发送的消息从该外部实体获取消息,则不存在内存可见性问题.
情况可能是这些操作没有以这种方式编程,或者外部实体没有假定的依赖关系,但可能是错误位于完全不同的区域,但我们无法告诉您未发布相关代码,例如密钥不匹配或打印代码错误.但不管它是什么,它都不会被volatile变量修复.
| 归档时间: |
|
| 查看次数: |
794 次 |
| 最近记录: |