Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 对Java中的安全发布和可见性感到困惑,尤其是对不可变对象_Multithreading_Concurrency_Thread Safety_Visibility_Immutability - Fatal编程技术网

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

Multithreading 对Java中的安全发布和可见性感到困惑,尤其是对不可变对象,multithreading,concurrency,thread-safety,visibility,immutability,Multithreading,Concurrency,Thread Safety,Visibility,Immutability,当我阅读Brian Goetz的《Java并发性实践》时,我记得他在关于可见性的一章中说过:“另一方面,即使不使用同步来发布对象引用,也可以安全地访问不可变对象” 我认为这意味着,如果发布一个不可变的对象,所有字段(包括可变的最终引用)对可能使用它们的其他线程都是可见的,并且至少是到该对象完成构造时为止的最新字段 现在,我读到了 说到这里,如果线程构造了一个不可变的对象(也就是只包含final字段的对象),那么,如果要确保其他所有线程都能正确地看到它,通常仍需要使用同步。没有其他方法可以确保第二

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

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

现在,我读到了 说到这里,如果线程构造了一个不可变的对象(也就是只包含final字段的对象),那么,如果要确保其他所有线程都能正确地看到它,通常仍需要使用同步。没有其他方法可以确保第二个线程能看到对不可变对象的引用。程序从最终字段获得的保证应经过仔细的处理,并加上深入细致的处理l了解如何在代码中管理并发性。”

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

我还读到,如果所有字段都是final,那么即使对象不是不可变的,我们也可以确保安全发布。 例如,由于这种保证,我一直认为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 == null)
            throw new IllegalArgumentException("No such ID: " + id);
        loc.x = x;
        loc.y = y;
    }

    private static Map<String, MutablePoint> deepCopy(
            Map<String, MutablePoint> m) {
        Map<String, MutablePoint> result =
            new HashMap<String, MutablePoint>();
        for (String id : m.keySet())
            result.put(id, new MutablePoint(m.get(id)));
        return Collections.unmodifiableMap(result);
    }
}
public class MutablePoint { /* Listing 4.5 */ }
@ThreadSafe
公共类监视器车辆追踪器{
@担保人(“本”)
私人最终地图位置;
公共监视器(
(地图位置){
this.locations=deepCopy(位置);
}
公共同步映射getLocations(){
返回副本(位置);
}
公共同步可变点getLocation(字符串id){
可变点位置=位置。获取(id);
返回loc==null?null:新可变点(loc);
}
公共同步的void setLocation(字符串id,int x,int y){
可变点位置=位置。获取(id);
如果(loc==null)
抛出新的IllegalArgumentException(“无此类ID:+ID”);
loc.x=x;
位置y=y;
}
私有静态映射副本(
地图(m){
映射结果=
新的HashMap();
对于(字符串id:m.keySet())
结果.put(id,新可变点(m.get(id));
返回集合。不可修改映射(结果);
}
}
公共类可变点{/*清单4.5*/}
例如,在这个代码示例中,如果最终保证为false,并且某个线程创建了该类的实例,那么对该对象的引用不为null,但另一个线程使用该类时字段位置为null,该怎么办


再一次,我不知道哪一个是正确的,或者我是否碰巧误解了这篇文章或戈茨

这个问题以前已经被回答过好几次了,但我觉得这些答案中有很多是不充分的。见:

  • 等等
简言之,Goetz在链接的JSR133常见问题页面中的陈述更为“正确”,尽管与您的思维方式不同

当Goetz说不可变对象即使在没有同步的情况下发布也可以安全使用时,他的意思是说对不同线程可见的不可变对象可以保证保留其原始状态/不变量,其他所有的都保持不变。换句话说,正确同步的发布对于保持状态一致性是不必要的

在JSR-133常见问题中,当他说:

您希望确保所有其他线程(sic)都能正确地看到它

他指的不是不变对象的状态。他的意思是,您必须同步发布,以便另一个线程看到对不可变对象的引用。这两条语句所讨论的内容有一个微妙的区别:JCIP指的是状态一致性,而FAQ页面指的是对不可变对象引用的访问

您提供的代码示例实际上与Goetz在此处所说的内容无关,但为了回答您的问题,如果对象正确初始化
final
字段将保持其预期值(请注意初始化和发布之间的差异)。代码示例还同步对
位置
字段的访问,以确保对
最终
字段的更新是线程安全的

事实上,为了进一步说明,我建议您查看JCIP清单3.13(
VolatileCachedFactorizer
)。请注意,尽管
OneValueCache
是不可变的,但它存储在
volatile
字段中。为了说明常见问题解答语句,
VolatileCachedFactorizer
如果没有
volatile
,将无法正常工作。“同步”是指使用
volatile
字段,以确保对其进行的更新对其他线程可见

说明第一条JCIP语句的一个好方法是删除
volatile
。在这种情况下,
cachedFactoryer
将无法工作。考虑一下:如果一个线程设置了一个新的缓存值,但另一个线程试图读取该值,字段不是“代码>易失性< /代码>?读卡器可能看不到更新的
OneValueCache
。但是,回想一下Goetz引用了不可变对象的状态,如果读线程碰巧看到存储在
缓存
中的
OneValueCache
的最新实例,那么该实例的状态将是可见的,并且构造正确

因此,尽管有可能丢失对
缓存的更新
,但是不可能丢失
OneValueCache
的状态(如果已读取),因为它是不可变的。我建议读t