简介
假设我有一个 ConcurrentHashMap 单例:
public class RecordsMapSingleton {
private static final ConcurrentHashMap<String,Record> payments = new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, Record> getInstance() {
return payments;
}
}
然后我有来自不同来源的三个后续请求(全部由不同线程处理)。
第一个服务发出请求,获取单例,创建 Record 实例,生成唯一 ID 并将其放入 Map,然后将此 ID 发送给另一个服务。
然后第二个服务使用该 ID 发出另一个请求。它获取单例,找到 Record 实例并修改它。
最后(可能半小时后)第二个服务发出另一个请求,以便进一步修改 Record。
问题
在一些非常罕见的情况下,我遇到了 heisenbug .在日志中我可以看到,第一个请求成功地将 Record 放入 Map 中,第二个请求通过 ID 找到它并修改了它,然后第三个请求试图通过 ID 找到 Record,但什么也没找到(get() 返回了 null)。
我发现关于 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.
来自 here .如果我做对了,它的字面意思是,get() 可以返回 Map 中实际存在的任何值,只要它不会破坏 happens-before不同线程中 Action 之间的关系。
在我的例子中,它是这样应用的:如果第三个请求不关心在处理第一个和第二个过程中发生了什么,那么它可以从 Map 中读取 null。
它不适合我,因为我真的需要从 Map 获取最新的实际 Record。
我尝试了什么
于是我开始思考,如何在后续的Map修改之间形成happens-before关系;并提出了想法。金力士says (在 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).
那么,让我们假设,我将像这样修改我的单例:
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;
}
}
然后,在每次修改 Map 或 Record 之后,我将调用 incrementRevision() 并且在从 Map 读取之前调用 I'我将调用 getRevision()。
问题
由于 heisenbugs 的性质,再多的测试也不足以证明这个解决方案是正确的。而且我不是并发方面的专家,所以无法正式验证。
有人可以同意,遵循这种方法可以保证我始终从 ConcurrentHashMap 中获取最新的实际值吗?如果此方法不正确或看起来效率低下,您能否推荐我其他方法?
最佳答案
您的方法不会奏效,因为您实际上又在重复同样的错误。由于 ConcurrentHashMap.put 和 ConcurrentHashMap.get 将创建一个happens before 关系但没有时间顺序保证,因此同样适用于您的读取和写入到 volatile 变量。它们形成一种happens before关系但没有时间顺序保证,如果一个线程碰巧在另一个线程执行put之前调用get,同样适用于volatile 读取将在 volatile 写入之前发生。除此之外,您还添加了另一个错误,因为将 ++ 运算符应用于 volatile 变量不是原子的。
volatile 变量的保证并不比 ConcurrentHashMap 更强。 It’s documentation明确指出:
Retrievals reflect the results of the most recently completed update operations holding upon their onset.
The JLS states外部操作是关于 program order 的线程间操作:
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.
简单地说,如果一个线程放入 ConcurrentHashMap 并向外部实体发送消息,而第二个线程在收到来自外部实体的消息后从同一个 ConcurrentHashMap 获取取决于先前发送的消息的外部实体,不会有内存可见性问题。
可能是因为这些操作没有以这种方式编程,或者外部实体没有假定的依赖关系,但也可能是错误出在完全不同的区域,但我们不能告诉你没有发布相关代码,例如 key 不匹配或打印代码错误。但无论它是什么,它都不会被 volatile 变量固定。
关于java - 如何保证ConcurrentHashMap的get()总是返回最新的实际值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29771770/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返