正确的方法进行AtomicReference持有的对象的相等和哈希码

时间:2018-12-21 11:58:59

标签: java multithreading

AtomicReferenceObjects.equals()Objects.hash()不兼容。

    AtomicReference<String> ref1 = new AtomicReference<>("hi");
    AtomicReference<String> ref2 = new AtomicReference<>("hi");
    System.out.println(Objects.equals(ref1, ref2)); //false
    System.out.println(Objects.equals(ref1.get(), ref2.get())); //true but potentially not thread safe
    System.out.println(Objects.hash(ref1) == Objects.hash(ref2)); //false
    System.out.println(Objects.hash(ref1.get()) == Objects.hash(ref2.get())); //true but potentially not thread safe

检查AtomicReference持有的两个对象是否相等并以一种既可行又安全的方式计算相同哈希的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

  

检查两个[变量]是否相等...以一种既有效又线程安全的最佳方式是什么?

这个问题没有任何意义。假设您有一个线程A要测试变量,而线程B可以随时更改其中一个变量而不会发出警告。

您想确定变量是否相等?好那怎么了如果您的“线程安全”相等性测试说它们相等,那么什么导致线程B在执行相等性测试的时间与对信息进行操作之间的时间中更改它们之一。

知道两个变量在某个时刻是否相等没有任何价值:您想知道在程序处于某种特定状态时变量是否相等,然后您想做状态改变之前的一些事情

因此,是的,您需要synchronized在一个锁定对象上,但这不仅是为了保护相等性测试。它必须防止程序在测试变量的整个过程中更改您关心的状态,然后根据测试结果执行某些操作。

答案 1 :(得分:0)

您想在equals期间阻止两个引用进行操作,从而使equals具有线程安全性。

我认为您不能仅通过AtomicReference来执行此操作,因为它不负责外部同步。您必须通过控制访问线程来手动执行此操作。

答案 2 :(得分:0)

首先,我想对以下声明提出质疑:

  

AtomicReferenceObjects.equals()Objects.hash()不兼容。

实际上,AtomicReference(AR)确实在至少两种意义上起作用:

  1. AR符合隐含规范。 javadocs表明equalshashCode方法没有被覆盖,因此这些方法的语义由Object定义。因此,AR对象具有equals的“身份”语义;也就是说,只有当两个AR对象是同一对象时,它们才是“相等”。

  2. AR的行为与标准类库定义的其他可变类型一致:数组,StringBuilder,其他Atomic*类型等等。

我将授予您AR的行为可能对您尝试执行的操作没有帮助。但是您会发现Objects.equals()Objects.hash()在我提到的其他可变类型上也存在相同的问题。


比较一对原子引用的包装值的正确方法是:

  Objects.equals(ref1.get(), ref2.get());

您(正确地)声明这不是线程安全的,但是(我认为)对于原子类型是期望的。通常,两个原子操作的序列是非原子的。通常,要将两个操作作为一个原子整体执行,您需要执行某种形式的锁定。

因此,一种简单(但有缺陷)的方法是这样做:

  synchronized (ar1) {
      synchronized (ar2) {
           result = ar1.get() == ar2.get();
      }
  }

缺点是存在死锁的危险……如果两个线程在ar1ar2互换的情况下同时执行上述代码。

该缺陷的一个(假设的)修复方法是确保线程以相同的顺序锁定ar1ar2。您可以(几乎总是)通过使用ar1.hashCode()ar2.hashCode()为您提供一致的订单;例如

  int h1 = ar1.hashCode();
  int h2 = ar2.hashCode();
  if (h1 < h2) {
      synchronized (ar1) {
          synchronized (ar2) {
               result = ar1.get() == ar2.get();
      }
  } else if (h1 > h2) {
      synchronized (ar1) {
          synchronized (ar2) {
               result = ar1.get() == ar2.get();
      }
  } else if (ar1 == ar2) {
      result = true;
  } else {
      // h1 == h2, but ar1 != ar2
  }

但是正如您所看到的,在极少数情况下,hashCode排序不起作用。

我不知道是否有 该子案例的解决方案...使用互斥锁。


另一种解决方法是指出期望您可以这样做实际上是不合理的。就是这个即使您能够测试两个AtomicReference对象确实引用了同一对象,该结果也仅在特定的时间有效。测试完成后的瞬间,某些线程可能会更改其中一个AR,从而使结果无效。

最重要的是,该测试无用,因为它不会告诉您应用程序可以依赖的任何内容。因此(IMO)实施它毫无意义,而担心种族状况则显得毫无意义。

实用的替代方法是对没有潜在死锁问题的Lock或互斥对象使用外部锁定。