5 安全发布

2018-09-21  本文已影响9人  史小猿

不安全的发布

这种发布会导致其他线程看到尚未未构建完成的对象,另一个线程在调用assertSanity 方法可能会抛出AssertionError(stackoverflow里的相关问题)

/**
 * StuffIntoPublic
 * <p/>
 * Unsafe publication
 *
 * @author Brian Goetz and Tim Peierls
 */
public class StuffIntoPublic {
    public Holder holder;

    public void initialize() {
        holder = new Holder(42);
    }
}
/**
 * Holder
 * <p/>
 * Class at risk of failure if not properly published
 *
 * @author Brian Goetz and Tim Peierls
 */
public class Holder {
    private int n;

    public Holder(int n) {
        this.n = n;
    }

    public void assertSanity() {
        if (n != n)
            throw new AssertionError("This statement is false.");
    }
}

不可变对象与初始化安全性

由于不可变对象是一种非常重要的对象,由于java内存模式为不可变对象提供了一种特殊的初始化安全保证。(如果将Holder里的n声明为final类型,那么Holder是不可变的,从而避免出现未正确发布的问题)我们知道即使一个对象的引用对于其他线程是可见的,也并不意味对象状态对于其他线程是可见的。为了确保状态一致得必须使用同步。

任何线程都可以在不需要额外同步的情况下去访问不可变对象,即使在发布这个对象时没有使用同步。这种保证还将延续到不可变对象的final域。在没有额外同步的情况下,也可以安全的访问所有的final域。然而,如果final域所指向的是一个可变对象,那么在访问这些域所指向的对象的状态仍然需要额外同步。

安全发布常用模型

可变对象可以用安全的方式来发布

事实不可变对象

技术上是可变的,不满足不可变对象的要求,但是其状态确实在发布后不会改变,这种对象称为事实不可变对象。
在没有额外同步的状态下任何线程都可以使用被安全发布的事实不可变对象


可变对象

如果对象在构造后可以修改,那么安全发布只能确保“发布当时”状态的可见性。对于可变对象,不仅在发布对象时使用同步,而且在每次对象访问时同样需要使用同步来确保后续操作的可见性。要安全地共享可变对象,这些对象就必须被安全地发布,并且必须是某个线程安全的或者由某个锁保护起来。

对象的发布需求取决于它的可变性:

安全的共享对象

当获得对象的一个引用时,你需要知道在这个引用上可以执行哪些操作。在使用它之前是否需要获得一个锁?是否可以修改他的状态,或者只能读取它?许多并发错误都是由于没有理解共享对象的这些“既定规则”而导致的。当发布一个对象时,必须明确的说明对象的访问方式。

在并发程序中使用和共享对象时,可以使用一些实用的策略,包括:

上一篇下一篇

猜你喜欢

热点阅读