草庐IT

java - 为 Unsafe.putOrdered*() 的发布实现获取?

coder 2024-03-13 原文

您认为在 Java 中实现发布/获取对的获取部分的最佳正确方法是什么?

我正在尝试使用经典的发布/获取语义(没有 StoreLoad 并且没有跨线程的顺序一致性)对我的应用程序中的一些操作进行建模。

有几种方法可以在 JDK 中实现大致相当于商店发布的效果。 java.util.concurrent.Atomic*.lazySet() 和底层 sun.misc.Unsafe.putOrdered*() 是最常被引用的方法。但是,没有明显的方法来实现加载获取。

  • 允许 lazySet() 的 JDK API 大多在内部使用 volatile 变量,因此它们的存储发布与易变加载配对。从理论上讲, volatile 加载应该比加载获取更昂贵,并且在之前的存储发布的上下文中不应提供比纯加载获取更多的东西。

  • sun.misc.Unsafe 不提供 getAcquire()* 等价于 putOrdered*() 方法,即使此类获取方法计划用于即将推出的 VarHandles API。

  • 听起来可行的是简单加载,然后是 sun.misc.Unsafe.loadFence()。我在其他任何地方都没有看到这一点,这有点令人不安。这可能与它是一个非常丑陋的 hack 这一事实有关。

附言我很清楚 JMM 没有涵盖这些机制,它们不足以维持顺序一致性,并且它们创建的操作不是同步操作(例如,我知道它们会破坏 IRIW)。我还了解到,Atomic*/Unsafe 提供的商店发布最常用于急切清空引用或在生产者/消费者场景中,作为某些重要索引的优化消息传递机制。

最佳答案

volatile read 正是您要找的。

事实上,对应的volatile操作已经有release/acquire语义(否则happens-before对于paired volatile write-read是不可能的),但是paired volatile操作不仅要顺序一致(~happens-before),还要他们应该在total synchronization order ,这就是为什么 StoreLoad在 volatile write 之后插入屏障:以保证对不同位置的 volatile 写入的总顺序,因此所有线程都将以相同的顺序看到这些值。

volatile 读取具有获取语义:proof来自热点代码库,Doug Lea 在 JSR-133 cookbook 中也有直接推荐(每次 volatile 读取后的 LoadLoadLoadStore 障碍)。

Unsafe.loadFence()也具有获取语义(proof),但不用于读取值(您可以对普通 volatile 读取执行相同的操作),而是用于防止对后续 volatile 读取重新排序普通读取。这用于 StampedLock用于乐观阅读(参见 StampedLock#validate 方法实现和用法)。

在评论中讨论后更新。

让我们检查一下 Unsafe#loadStore()和 volatile read 是一样的,都具有 acquire 语义。

我正在查看热点 C1 compiler source code以避免通读 C2 中的所有优化。 它将字节码(实际上,不是字节码,而是它的解释器表示)转换为 LIR(低级中间表示),然后根据目标微体系结构将图形转换为实际操作码。

Unsafe#loadFenceintrinsic其中有 _loadFence别名。在 C1 LIR generator它生成这个:

case vmIntrinsics::_loadFence :
if (os::is_MP()) __ membar_acquire();

哪里__是生成 LIR 的宏。

现在让我们看看volatile read implementation在同一个 LIR 发生器中。它会尝试插入空值检查、检查 IRIW、检查我们是否在 x32 上并尝试读取 64 位值(以使用 SSE/FPU 创造一些魔力),最后,将我们引向相同的代码:

if (is_volatile && os::is_MP()) {
    __ membar_acquire();
}

汇编程序生成器然后插入特定于平台的获取指令 here .

看具体实现(这里没有链接,都可以在src/cpu/{$cpu_model}/vm/c1_LIRAssembler_{$cpu_model}.cpp中找到)

  • SPARC

    void LIR_Assembler::membar_acquire() {
        // no-op on TSO
    }
    
  • x86

    void LIR_Assembler::membar_acquire() {
        // No x86 machines currently require load fences
    }
    
  • Aarch64(弱内存模型,应该存在障碍)

    void LIR_Assembler::membar_acquire() {
        __ membar(Assembler::LoadLoad|Assembler::LoadStore);
    }
    

    根据 aarch architecture description这样的 membar 将被编译为 dmb ishld加载后指令。

  • PowerPC(也是弱内存模型)

    void LIR_Assembler::membar_acquire() {
        __ acquire();
    }
    

    然后转换为特定的 PowerPC 指令 lwsync .根据comments lwsync在语义上等同于

    lwsync orders Store|Store, Load|Store, Load|Load, but not Store|Load

    但只要 PowerPC 没有任何较弱的障碍,这是在 PowerPC 上实现获取语义的唯一选择。

结论

volatile 读取和 Unsafe#loadFence()在内存排序方面是相同的(但在可能的编译器优化方面可能不同),在最流行的 x86 上它是无操作的,并且 PowerPC 是唯一受支持的架构,没有精确的获取障碍。

关于java - 为 Unsafe.putOrdered*() 的发布实现获取?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37105390/

有关java - 为 Unsafe.putOrdered*() 的发布实现获取?的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  4. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  5. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  6. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  7. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  8. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用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

  9. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  10. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

随机推荐