相关疑难解决方法(0)

不变性和重新排序

下面的代码(Java Concurrency in Practice清单16.3)由于显而易见的原因而不是线程安全的:

public class UnsafeLazyInitialization {
    private static Resource resource;

    public static Resource getInstance() {
        if (resource == null)
            resource = new Resource();  // unsafe publication
        return resource;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,几页之后,在第16.3节中,他们指出:

UnsafeLazyInitialization如果 Resource是不可变的,实际上是安全的.

我不明白这句话:

  • 如果Resource是不可变的,那么观察resource变量的任何线程都将看到它为null或完全构造(由于Java内存模型提供的最终字段的强保证)
  • 但是,没有什么可以阻止指令重新排序:特别是两个读取resource可以重新排序(在一个读取if和一个读取return).因此,线程可以resourceif条件中看到非null 但返回空引用(*).

我认为UnsafeLazyInitialization.getInstance()即使Resource是不可变的也可以返回null .是这样的,为什么(或为什么不)?


(*)为了更好地理解我关于重新排序的观点,Jeremy Manson的博客文章是JLS并发的第17章的作者之一,解释了如何通过良性数据竞争安全地发布String的哈希码以及如何删除使用局部变量可能导致哈希码错误地返回0,因为可能的重新排序非常类似于我上面描述的:

我在这里做的是添加一个额外的读取:在返回之前的第二次读取哈希.听起来很奇怪,并且不太可能发生,第一次读取可以返回正确计算的散列值,第二次读取可以返回0!这在内存模型下是允许的,因为该模型允许对操作进行大量重新排序.第二次读取实际上可以在您的代码中移动,以便您的处理器在第一次读取之前完成!

java concurrency multithreading java-memory-model

42
推荐指数
1
解决办法
2545
查看次数

对 Java 中的安全发布和可见性感到困惑,尤其是不可变对象

当我阅读 Brian Goetz 的《Java 并发实践》时,我记得他在有关可见性的章节中说过“另一方面,即使不使用同步来发布对象引用,也可以安全地访问不可变对象”。

我认为这意味着,如果您发布一个不可变的对象,则所有字段(包括可变的最终引用)对于可能使用它们的其他线程都是可见的,并且至少是该对象完成构造时的最新状态。

现在,我在https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html中读到“现在,说了所有这些,如果在线程构造一个不可变对象之后(即,仅包含最终字段的对象),如果您想确保所有其他线程都能正确看到它,那么通常仍然需要使用同步。没有其他方法可以确保,例如,第二个线程将看到对不可变对象的引用。程序从最终字段获得的保证应该通过深入而仔细地理解代码中如何管理并发性来仔细调整。

他们似乎互相矛盾,我不知道该相信哪一个。

我还读到,如果所有字段都是最终字段,那么即使对象本身不是不可变的,我们也可以确保安全发布。例如,由于这个保证,在发布此类的对象时,我一直认为实践中 Brian Goetz 的并发中的这段代码是没问题的。

@ThreadSafe
public class MonitorVehicleTracker {
    @GuardedBy("this")
    private final Map<String, MutablePoint> locations;

    public MonitorVehicleTracker(
            Map<String, MutablePoint> locations) {
        this.locations = deepCopy(locations);
    }

    public synchronized Map<String, MutablePoint> getLocations() {
        return deepCopy(locations);
    }

    public synchronized MutablePoint getLocation(String id) {
        MutablePoint loc = locations.get(id);
        return loc == null ? null : new MutablePoint(loc);
    }

    public synchronized void setLocation(String id, int x, int y) {
        MutablePoint loc = locations.get(id);
        if (loc …
Run Code Online (Sandbox Code Playgroud)

concurrency multithreading visibility immutability thread-safety

4
推荐指数
1
解决办法
882
查看次数