我正在使用具有 ConcurrentHashMap 形式的对象存储的现有代码。映射中存储了可变对象,供多个线程使用。按照设计,没有两个线程会尝试同时修改一个对象。我担心的是线程之间修改的可见性。
目前,对象的代码在“setter”(由对象本身保护)上同步。 “ setter/getter ”没有同步,成员也不是易变的。对我来说,这意味着无法保证可见性。然而,当一个对象被修改时,它被重新放置回到 map 中(再次调用put()方法,相同的键)。这是否意味着当另一个线程将对象从 map 中拉出时,它会看到修改?
我在 stackoverflow 上研究过这个,在 JCIP ,并在 java.util.concurrent 的包描述中。我想我基本上把自己弄糊涂了……但导致我问这个问题的最后一根稻草来自包裹描述,它指出:
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.
关于我的问题,“ Action ”是否包括在 re-put() 之前对映射中存储的对象的修改?如果所有这些确实导致跨线程的可见性,这是一种有效的方法吗?我对线程比较陌生,非常感谢您的意见。
编辑:
谢谢大家的回复!这是我在 StackOverflow 上的第一个问题,它对我很有帮助。
我必须选择 ptomli的回答,因为我认为它最清楚地解决了我的困惑。也就是说,在这种情况下建立“先于发生”关系并不一定会影响修改的可见性。关于我在文中描述的实际问题,我的“标题问题”构造不当。 ptomli现在的答案与我在 JCIP 中读到的内容一致:“为确保所有线程都能看到共享可变变量的最新值,读写线程必须在公共(public)锁上同步”(第 37 页)。将对象重新放回映射中不会为插入对象的成员的修改提供此公共(public)锁。
我很欣赏所有关于更改的提示(不可变对象(immutable对象)等),并且我全心全意地同意。但是对于这种情况,正如我所提到的,由于仔细的线程处理,没有并发修改。一个线程修改一个对象,另一个线程稍后读取该对象(CHM 是对象传送器)。在我提供的情况下,我认为 CHM 不足以确保稍后执行的线程将看到第一个线程的修改。不过,我认为你们中的许多人正确回答了标题问题。
最佳答案
您在每次写入对象后调用 concurrHashMap.put。但是,您没有指定在每次读取之前还调用 concurrHashMap.get。这是必要的。
所有形式的同步都是如此:您需要在两个线程中都有一些“检查点”。只同步一个线程是没有用的。
我没有检查 ConcurrentHashMap 的源代码以确保 put 和 get 触发 happens-before,但它们应该是合乎逻辑的。
但是,即使您同时使用了 put 和 get,您的方法仍然存在问题。当您修改一个对象并且它在 put 之前被另一个线程使用(处于不一致状态)时,就会出现问题。这是一个微妙的问题,因为您可能认为旧值会被读取,因为它还没有被 put 并且它不会导致问题。问题是当你不同步时,你不能保证得到一个一致的旧对象,而是行为是未定义的。 JVM 可以随时更新其他线程中对象的任何部分。只有在使用某些显式同步时,您才能确保以一致的方式跨线程更新值。
你可以做什么:
(1) 在代码的任何地方同步对你的对象的所有访问(getters 和setters)。小心 setter :确保您不能将对象设置为不一致的状态。例如,当设置名字和姓氏时,有两个同步的 setter 是不够的:您必须同时为两个操作获取对象锁。
或者
(2) 当您在 map 中放置一个对象时,放置一个深拷贝而不是对象本身。这样其他线程将永远不会读取处于不一致状态的对象。
编辑:
我刚注意到
Currently the objects' code has synchronization on the "setters" (guarded by the object itself). There is no synchronization on the "getters" nor are the members volatile.
这样不好。正如我上面所说,只在一个线程上同步根本就不是同步。您可能会在所有编写器线程上进行同步,但谁在乎呢,因为读者不会获得正确的值。
关于java - 将对象重新放入 ConcurrentHashMap 是否会导致 "happens-before"内存关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7809131/
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test
我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que
我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file
当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www