无需同步即可共享数据 - 此代码线程是否安全?

时间:2017-10-24 09:11:09

标签: java concurrency thread-safety

我在Peter Lawry的博客here上找到了这段代码。他提到这个课程不需要任何进一步的同步。

我正在努力提高我的并发知识以及如何避免不必要的同步,所以我试图从java内存模型的角度来思考如何推理这个案例。

对字符串数组的引用是final,字符串本身是不可变的,但对数组中包含的字符串的引用是可变的

  • 在另一个线程更新了值之后,一个线程是否仍然可以看到null,理论上是不是至少在理论上可能?
  • 或者我们不关心字符串是否被多次实习?
  • 或者JVM是否提供了一些我遗漏的额外保证?

    public class StringInterner {
       private final String[] interner;
       private final int mask;
    
       public StringInterner(int capacity) {
          int n = Maths.nextPower2(capacity, 128);
          interner = new String[n];
          mask = n - 1;
       }
    
       private static boolean isEqual(@Nullable CharSequence s, @NotNull CharSequence cs) {
          if (s == null)
             return false;
          if (s.length() != cs.length())
             return false;
          for (int i = 0; i < cs.length(); i++)
             if (s.charAt(i) != cs.charAt(i))
                return false;
          return true;
       }
    
       @NotNull
       public String intern(@NotNull CharSequence cs) {
          long hash = 0;
          for (int i = 0; i < cs.length(); i++)
             hash = 57 * hash + cs.charAt(i);
          int h = (int) Maths.hash(hash) & mask;
          String s = interner[h];
          if (isEqual(s, cs))
             return s;
          String s2 = cs.toString();
          return interner[h] = s2;
       }
    }
    

2 个答案:

答案 0 :(得分:2)

在你的情况下,我认为我们并不关心字符串是否被实施两次。内存模型注意在分配数组值时不会发生任何恶意。以下是相关问题:java array thread-safety

从并发的角度来看,它没有同步,因为数据是安全的。因此,该类在并发访问中正常工作。

如果您希望严格执行实习生只有在您需要同步但有其价格时才会发生。这取决于您的用例对您的正确性。 (正如gudok所指出的那样:独立于并发仍然实际上因为散列而不止一次发生)

答案 1 :(得分:0)

我同意关于公开性 Sorontur 评论。此代码可能会产生意外结果(重现场景可能会很棘手)。似乎intern方法不是线程安全的。多个线程可以在多个内核上并行运行,通常每个内核都有自己的缓存。如果一个线程更新intern中的任何变量,它将立即更新其缓存,但其他核心的缓存不会同时更新,需要一段时间,同时其他线程可能会使用旧值。因此,为了解决这种情况,您可以使用volatile变量,但这会影响性能。因此,shared memory model上的多线程是性能和效率之间的权衡。

注意:我认为在并发线程上可以看到意外行为,它并不特定于并行执行