简体   繁体   English

如何保证ConcurrentHashMap的get()始终返回最新的实际值?

[英]How to guarantee get() of ConcurrentHashMap to always return the latest actual value?

Introduction 介绍
Suppose I have a ConcurrentHashMap singleton: 假设我有一个ConcurrentHashMap单例:

public class RecordsMapSingleton {

    private static final ConcurrentHashMap<String,Record> payments = new ConcurrentHashMap<>();

    public static ConcurrentHashMap<String, Record> getInstance() {
        return payments;
    }

}

Then I have three subsequent requests (all processed by different threads) from different sources. 然后,我有来自不同来源的三个后续请求(均由不同的线程处理)。
The first service makes a request, that gets the singleton, creates Record instance, generates unique ID and places it into Map , then sends this ID to another service. 第一个服务发出一个请求,该请求获取单例,创建Record实例,生成唯一ID并将其放入Map ,然后将此ID发送给另一个服务。
Then the second service makes another request, with that ID. 然后,第二个服务使用该ID发出另一个请求。 It gets the singleton, finds Record instance and modifies it. 它获取单例,找到Record实例并对其进行修改。
Finally (probably after half an hour) the second service makes another request, in order to modify Record further. 最后(可能是半小时后),第二个服务再次发出请求,以进一步修改Record

Problem 问题
In some really rare cases, I'm experiencing heisenbug . 在某些非常罕见的情况下,我遇到了heisenbug In logs I can see, that first request successfully placed Record into Map , second request found it by ID and modified it, and then third request tried to find Record by ID, but found nothing ( get() returned null ). 在日志中,我可以看到,第一个请求成功将Record放入Map ,第二个请求按ID找到并修改了它,然后第三个请求尝试按ID查找Record,但未找到任何内容( get()返回null )。
The single thing that I found about ConcurrentHashMap guarantees, is: 我发现有关ConcurrentHashMap的唯一保证是:

Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread. 在将对象放入任何并发集合之前,线程中的操作发生在访问另一个线程中的元素或从集合中删除该元素之后的操作。

from here . 这里 If I got it right, it literally means, that get() could return any value that actually was sometime into Map, as far as it doesn't ruin happens-before relationship between actions in different threads. 如果我做对了,从字面上看,它意味着get()可以返回某个时候实际上在Map中的任何值,只要它不会破坏happens-before在不同线程之间的动作之间的关系即可。
In my case it applies like this: if third request doesn't care about what happened during processing of first and second, then it could read null from Map . 在我的情况下,它的用法是这样的:如果第三个请求不关心第一个和第二个处理期间发生了什么,那么它可以从Map读取null

It doesn't suit me, because I really need to get from Map the latest actual Record . 它不适合我,因为我真的需要从Map获取最新的实际Record

What have I tried 我尝试了什么
So I started to think, how to form happens-before relationship between subsequent Map modifications; 因此我开始思考,如何在后续Map修改之间建立关系happens-before and came with idea. 并产生了想法。 JLS says (in 17.4.4) that: JLS (在17.4.4)认为:

A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order). 对易失性变量v的写操作(第8.3.1.4节)与任何线程对v的所有后续读取进行同步(其中“后续”是根据同步顺序定义的)。

So, let's suppose, I'll modify my singleton like this: 因此,让我们假设,我将像这样修改我的单身人士:

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;
    }

}

Then, after each modification of Map or Record inside, I'll call incrementRevision() and before any read from Map I'll call getRevision() . 然后,每次修改后MapRecord里面,我会打电话给incrementRevision()并从地图我任何读前会打电话给getRevision()

Question
Due to nature of heisenbugs no amount of tests is enough to tell that this solution is correct. 由于heisenbugs的性质,无法进行大量测试就足以证明该解决方案是正确的。 And I'm not an expert in concurrency, so couldn't verify it formally. 而且我不是并发方面的专家,因此无法正式验证它。

Can someone approve, that following this approach guarantees that I'm always going to get the latest actual value from ConcurrentHashMap ? 有人可以认可,采用这种方法可以确保我始终从ConcurrentHashMap获取最新的实际值吗? If this approach is incorrect or appears to be inefficient, could you recommend me something else? 如果这种方法不正确或效率低下,您可以推荐我其他方法吗?

You approach will not work as you are actually repeating the same mistake again. 您的方法将不起作用,因为您实际上是在重复相同的错误。 As ConcurrentHashMap.put and ConcurrentHashMap.get will create a happens before relationship but no time ordering guaranty, exactly the same applies to your reads and writes to the volatile variable. 由于ConcurrentHashMap.putConcurrentHashMap.get将在发生关系之前创建事件,但没有时间顺序保证,因此对volatile变量的读取和写入完全相同。 They form a happens before relationship but no time ordering guaranty, if one thread happens to call get before the other performed put , the same applies to the volatile read that will happen before the volatile write then. 它们关系发生之前发生,但是没有时间顺序保证,如果一个线程碰巧在另一个执行put之前调用get ,则该情况同样适用于在volatile写入之前发生的volatile读取。 Besides that, you are adding another error as applying the ++ operator to a volatile variable is not atomic. 除此之外,您还添加了另一个错误,因为将++运算符应用于volatile变量不是原子的。

The guarantees made for volatile variables are not stronger than these made for a ConcurrentHashMap . volatile变量所做的保证并不比对ConcurrentHashMap所作的保证强。 It's documentation explicitly states: 它的文档明确指出:

Retrievals reflect the results of the most recently completed update operations holding upon their onset. 检索反映了自发生以来最新完成的更新操作的结果。

The JLS states that external actions are inter-thread actions regarding the program order : JLS声明外部动作是与程序顺序有关的线程间动作:

An inter-thread action is an action performed by one thread that can be detected or directly influenced by another thread. 线程间操作是由一个线程执行的操作,可以被另一个线程检测或直接影响该操作。 There are several kinds of inter-thread action that a program may perform: 程序可以执行几种类型的线程间操作:

  • External Actions . 外部行动 An external action is an action that may be observable outside of an execution, and has a result based on an environment external to the execution. 外部动作是在执行外部可以观察到的动作,其结果基于执行外部的环境。

Simply said, if one thread puts into a ConcurrentHashMap and sends a message to an external entity and a second thread gets from the same ConcurrentHashMap after receiving a message from an external entity depending on the previously sent message, there can't be a memory visibility issue. 简而言之,如果一个线程放入ConcurrentHashMap并向外部实体发送消息,而第二个线程在从外部实体接收到一条消息(取决于先前发送的消息)后从同一个ConcurrentHashMap获取消息,则无法看到内存问题。

It might be the case that these action aren't programmed that way or that the external entity doesn't have the assumed dependency, but it might be the case that the error lies in a completely different area but we can't tell as you didn't post the relevant code, eg the key doesn't match or the printing code is wrong. 可能是因为这些操作未采用这种方式进行编程,或者外部实体没有假定的依赖关系,但也可能是错误位于完全不同的区域,但我们无法如您所愿没有发布相关代码,例如密钥不匹配或打印代码错误。 But whatever it is, it won't be fixed by the volatile variable. 但是不管它是什么,它都不会由volatile变量解决。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何获取ConcurrentHashMap的最新视图? - How do I get the latest view of a ConcurrentHashMap? 如何获取ConcurrentHashMap的tailMap? - How to get tailMap for ConcurrentHashMap? ConcurrentHashMap值迭代器可以返回null - ConcurrentHashMap value iterator can it return null 如何在ConcurrentHashMap线程安全中更新值 - How to update a Value in a ConcurrentHashMap threadsafe 关于返回值,ConcurrentHashMap中的读取操作是否可靠? - Is read operation in ConcurrentHashMap reliable regarding the return value? 如何从此ConcurrentHashMap中删除值 <String,ConcurrentHashMap<String,ArrayList> &gt; - How to remove a value from this ConcurrentHashMap<String,ConcurrentHashMap<String,ArrayList>> 与 ConcurrentHashMap's.compute() 同步是否保证可见性? - Does synchronisation with ConcurrentHashMap's .compute() guarantee visibility? 如何从 BehaviorSubject 获取最新值? - How to get latest value from BehaviorSubject? ConcurrentHashMap:仅当当前值较小时,如何替换条目的值 - ConcurrentHashMap: how to replace the value of an entry only if the current value is smalle 如何获取搜索栏的进度值(实际进度值之前)? - How to get the progress value of seekbar, which was BEFORE the actual progress value?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM