Java 并发编程(二)对象的不变性和安全的发布对象

本文探讨了Java中对象的不变性和安全发布,不变性是线程安全的重要特性,通过final域和Volatile类型来保证。安全发布确保对象正确初始化后能被多个线程安全共享,介绍了多种安全发布模式,如静态初始化、volatile域和final域等。文章还讨论了可变对象的安全发布要求和并发访问时的同步策略。
摘要由CSDN通过智能技术生成

一、不变性

        满足同步需求的另一种方法是使用不可变对象(Immutable Object)。到目前为止,我们介绍了许多与原子性和可见性相关的问题,例如得到失效数据,丢失更新操作或光查到某个对象处于不一致的状态等等,都与多线程视图同时访问同一个可变的状态相关。如果对象的状态不会改变,那么这些问题与复杂性也就自然消失了。

        如果某个对象在被创建后其状态就不能被修改,那么这个对象就被成为不可变对象。线程安全型是不可变对象的固有属性之一,他们的不变性条件是由构造函数创建的,只要他们的状态不改变,那么这些不变性条件就能得以维持。

        不可变对象很简单。他们只有一种状态,并且该状态由构造函数来控制。在程序设计中一个最困难的地方就是判断复杂对象的可能状态。然而,判断不可变对象的状态却很简单。

        虽然在 Java 规范和 Java 内存模型中都没有给出不可变性的正式定义,但不可变性并不等于将对象中所有的域都声明为 final 类型,即使对象中所有的域都是 final 类型的,这个对象也仍然是可变的,因为在 final 类型的域中可以保存对可变对象的引用。

当满足以下条件时,对象才是不可变的:

  • 对象创建完之后其状态就不能修改
  • 对象的所有与都是 final 类型
  • 对象时正确创建的(创建期间没有 this 的逸出)
我们来分析下面这个类。
@Immutable
public final class ThreeStooges {
    private final Set<String> stooges = new HashSet<String>();

    public ThreeStooges() {
        stooges.add("Moe");
        stooges.add("Larry");
        stooges.add("Curly");
    }

    public boolean isStooge(String name) {
        return stooges.contains(name);
    }
}

        在不可变对象的内部仍可以使用可变对象来管理它们的状态,如 ThreeStooges 所示。尽管保存姓名的Set对象是可变的,但从ThreeStooges的设计中可以看到,在Set对象构造完成后无法对其进行修改。stooges是一个final类型的引用变量,因此所有的对象状态都通过一个final域来访问。最后一个要求是“正确地构造对象”,这个要求很容易满足,因为构造函数能使该引用由除了构造函数及其调用者之外的代码来访问。

        由于程序的状态总在不断地变化,你可能会认为需要使用不可变对象的地方不多,但实际情况并非如此。在“不可变的对象”与“不可变的对象引用”之间存在着差异。保存在不可变对象中的程序状态仍然可以更新,即通过将一个保存新状态的实例来“替换”原有的不可变对象。        

Final 域

        关键字 final 可以视为 C++ 中 const 机制的一种受限版本,用于构造不可变对象。final 类型的域是不能修改的(但如果 final 域所引用的对象时可变的,那么这些被引用的对象是可以修改的)。然而,在 Java 内存模型中,final 域还有着特殊的语义。final 域能确保初始化过程的安全性,从而可以不受限制的访问不可变对象,并在共享这些对象时无需同步。

注:个人理解为,final 字段一旦被初始化

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值