Java 锁定条件和特定值

Java 锁定条件和特定值,java,multithreading,Java,Multithreading,我已经开始阅读实践中的Java并发,但在继续之前,我有一个需要解决的想法 以这段代码为例。 我用番石榴()来检索锁(s)。 顺便说一句,我知道,正如Guava文档所述,这类似于使用ConcurrentHashMap public Object getValue(final String input) { final Lock inputLock = STRIPED.get(input); try { inputLock.lock(); Object valu

我已经开始阅读实践中的Java并发,但在继续之前,我有一个需要解决的想法

以这段代码为例。 我用番石榴()来检索
(s)。 顺便说一句,我知道,正如Guava文档所述,这类似于使用
ConcurrentHashMap

public Object getValue(final String input) {
   final Lock inputLock = STRIPED.get(input);

   try {
      inputLock.lock();
      Object value = VALUES.get(input);

      if (value == null) {
         value = buildValue(input);
         VALUES.put(input, value);
      }

      return value;
   } finally {
      inputLock.unlock();
   }
}
现在,它通过基于输入值锁定来工作。
但是,如果在大多数情况下使用相同的值调用
getValue(…)
方法,则我将不进行任何同步,因为同步仅用于第一次也是唯一一次写入


如何优化这段代码(如果可能)?

如果不想锁定这些现有值,只需移动
值。从锁定部分中取出
。然后在锁定时再次检查

public Object getValue(final String input) {
    Object value = VALUES.get(input);
    if (value == null) {
        Lock lock = STRIPED.get(input);
        try {
            lock.lock();
            value = VALUES.get(input);
            if (value == null) {
                value = buildValue(input);
                VALUES.put(input, value);
            }
        } finally {
            lock.unlock();
        }
    }
    return value;
}

如果您想了解锁定,那么您就走上了正确的道路

如果您想在关键应用程序中实际使用它,最好使用此模式的标准实现,例如Guava的LoadingCache,它重用ConcurrentHashMap的双重锁定逻辑,但也允许以线程安全的方式计算值


即使您不需要逐出逻辑,对于线程安全的惰性计算缓存来说,这仍然是一个很好的解决方案。

您尝试实现的功能可以使用Java 8中的ConcurrentHashMap完成

public Object getValue(final String input) {
    return chm.computeIfAbsent(input, ip -> buildValue(ip));
}

我不是番石榴专家,但通常您会通过某种形式的双重检查锁定(只要
标记为volatile)来实现这一点。您首先调用
值。get()
,然后如果返回null,则获取一个锁,然后从该锁中再次进行相同的调用。如果该调用也返回null,您可以继续,构建值,然后在再次释放锁之前将其插入到映射中。@MichaelBerry Oh Guava在这里并不重要。谢谢你的解释。你为什么用条纹呢?您可以使用ConcurrentHashMap computeIfAbsent实现同样的功能method@NeeravVadodaria这只是为了学习。正如我所说的,我知道我可以使用ConcurrentHashMap,但我想知道可能的优化是什么be@MichaelBerry寻找戈茨书中的“双重检查锁定”,他说这是一种需要避免的反模式。因此,我认为如果不完全切换策略,这段代码是无法优化的。这看起来有点“奇怪”,但如果这是唯一的方法,那么就没关系了。@LppEdd我不知道是否还有其他方法。关于这个方法的更多信息,您可以签出。我通常会选择单例的惰性类初始化方法。它没有那么冗长和冗余。如果它带有多线程,总会有一些冗长的东西。我最不喜欢的是双映射#get调用。这可能会成为一项繁重的操作,尤其对于使用旧版本Java 8的人来说,这可能非常有用。谢谢,嗨!值得一提的是,它仅适用于Java8+用户。