同步块有一个逻辑

synchronized block has a logic

特别需要根据字符串值创建线程监视器

例如:

    Map<String, String> values = new HashMap<>(); (instance variable)
    values.put("1", "one");values.put("2", "two");values.put("3", "three");


    void someMethod(String value) {
      synchronized(values.get(value) == null ? value : values.get(value)) {
        sout("I'm done");
      }
    }

这里的问题是同步块有一个三元运算符,允许吗?我没有收到任何 compile/run 时间异常或错误。

我不确定上面的代码是否真的是线程安全的,一次只有一个线程需要根据字符串值获取系统监视器。

请提供对此的想法。这是好的做法还是其他方法?

这种方法存在根本性问题。您正在访问 HashMap,它不是线程安全的, 进入 synchronized 块之前。如果地图在构建后有更新,则此方法无效。

当访问相同的数据时,使用相同的对象实例进行同步是至关重要的。

所以即使你在这里使用了线程安全的映射,使用values.get(value) == null? value: values.get(value)意味着使用变化的对象进行同步,当有映射更新时,有时它使用键,有时使用映射值,这取决于是否存在映射。即使密钥始终存在,它也可能使用不同的映射值。

它也与 Check-Then-Act 反模式有关,因为您首先检查 values.get(value) == null,然后在条件可能已经改变时使用 values.get(value)

切勿使用字符串进行同步,因为不同的字符串对象可能相同,因此在将它们用作 Map 的键时它们会映射到相同的数据,而同步会因不同的对象标识而失败。另一方面,字符串可以在 JVM 中自由共享,并且它们是字符串文字的情况,因此对字符串执行同步的不相关代码可能会相互阻塞。

有一个简单的解决方案,使用为此目的设计的工具。使用时

ConcurrentMap<String, String> values = new ConcurrentHashMap<>();

void someMethod(String string) {
    values.compute(string, (key,value) -> {
        if(value == null) value = key.toUpperCase(); // construct when not present
        // update value
        return value;
    });
}

字符串的相等性决定了互斥性,但本身不作为同步密钥。因此,相等的键提供了所需的阻塞,而不相关的代码,例如使用具有相似甚至相同键值的不同 ConcurrentHashMap 不受这些操作的影响。