`
623deyingxiong
  • 浏览: 190131 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

不可变对象(Immutable Objects)(Concurrency tutorial 6)

 
阅读更多
不可变对象(Immutable Objects)

如果一个对象的状态在其创建之后不能被改变,那我们就说它是不可变对象。在多线程编程中,最大程度地使用不可变对象可以实现简单又可靠的代码。

不可变对象在并发应用中特别有用。因为它们不会改变状态,它们在遇到线程干扰和不一致访问时也不会出错(译注:因为每个线程在试图改变一个对象时,实际上是得到了该对象在最新状态下的拷贝,例如String就是不可变对象)。

程序员经常不愿使用不可变对象,因为他们担心拷贝对象会降低性能。实际上,创建对象的资源耗费常被高估(overestimated),实际上这样的资源耗费完全可以被不可变对象提高的效率抵消掉。使用不可变对象可以降低因垃圾回收产生的对象管理开支,消除为保护可变对象不被污染的额外代码。

下面几个小节里将创建一个状态可变的类,然后演化成一个状态不可变的类。以此描述转换要遵守的规则以及不可变对象相对于可变对象的优势。

一个同步类的例子

SychronizedRGB类定义了表示颜色对象的集合。每一个对象代表一种颜色,每一种颜色用三个表示基本色的整数和一个描述颜色名称的字符串表示。

public class SynchronizedRGB {

    // Values must be between 0 and 255.
    private int red;
    private int green;
    private int blue;
    private String name;

    private void check(int red,
                       int green,
                       int blue) {
        if (red < 0 || red > 255
            || green < 0 || green > 255
            || blue < 0 || blue > 255) {
            throw new IllegalArgumentException();
        }
    }

    public SynchronizedRGB(int red,
                           int green,
                           int blue,
                           String name) {
        check(red, green, blue);
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.name = name;
    }

    public void set(int red,
                    int green,
                    int blue,
                    String name) {
        check(red, green, blue);
        synchronized (this) {
            this.red = red;
            this.green = green;
            this.blue = blue;
            this.name = name;
        }
    }

    public synchronized int getRGB() {
        return ((red << 16) | (green << 8) | blue);
    }

    public synchronized String getName() {
        return name;
    }

    public synchronized void invert() {
        red = 255 - red;
        green = 255 - green;
        blue = 255 - blue;
        name = "Inverse of " + name;
    }
}


SynchronizedRGB必须谨慎使用来避免读写不一致。譬如,一个线程这样执行时:
SynchronizedRGB color =
    new SynchronizedRGB(0, 0, 0, "Pitch Black");
...
int myColorInt = color.getRGB();      //Statement 1
String myColorName = color.getName(); //Statement 2


另一个线程在Statement 1之后,Statement 2之前调用color.set。那么myColorInt的值就与对应的myColorName不一致。为了避免这样的结果,两条语句必须绑定执行:
synchronized (color) {
    int myColorInt = color.getRGB();
    String myColorName = color.getName();
} 


这种不一致只会在可变对象上发生 ——对于不可变版本的SynchronizedRGB不会有问题。

定义不可变对象的一种策略
下面的规则说明了创建不可变对象的一种简单策略。并不是所有的不可变类都要遵循下面的规则,也并不是说这些类的创建者都是糊涂蛋——他们有充分的理由相信这些类的实例在创建之后不会改变状态。然而,这样的策略需要复杂的分析,初学者慎用。

  1. 不要提供setter方法(可能改变类数据成员的方法或者改变数据成员指向的对象的状态的方法)。
  2. 所有的数据成员修饰为private final。
  3. 不允许子类继承父类方法。最简单的方法就是将类声明为final。更复杂的方法是将构造器声明为private。使用工厂方法创建实例。
  4. 如果数据成员中存在有指向可变对象的,禁止这些被指向的对象状态被改变。
    • 不要提供方法改变可变对象。
    • 不要共享指向可变对象的引用。不要在构造器中存储指向可能发生改变的对象的引用。如果必要,创建这些对象的拷贝,转而存储这些拷贝的引用。同样地,当需要返回内部可变对象时,返回其拷贝,避免返回原始的内部对象引用。


将策略应用到SynchronizedRGB需要一下几步:
  1. 类里有两个setter方法。第一个set方法能随意转换对象状态,这在不可变类中根本不允许存在(and has no place in an immutable version of the class)。第二个,invert,可以改造为返回修改对象的拷贝,而不是修改当前对象。
  2. 所有的字段都是private的;它们应进一步地修饰为final。
  3. 类本身应修饰为final。
  4. 只有一个数据成员指向一个对象,且对象是其本身。因为其本身是不可变的,所以就没有必要再采取措施防止其包含的可变对象发生改变。


经过一些改变,我们得到了ImmutableRGB:
final public class ImmutableRGB {

    // Values must be between 0 and 255.
    final private int red;
    final private int green;
    final private int blue;
    final private String name;

    private void check(int red,
                       int green,
                       int blue) {
        if (red < 0 || red > 255
            || green < 0 || green > 255
            || blue < 0 || blue > 255) {
            throw new IllegalArgumentException();
        }
    }

    public ImmutableRGB(int red,
                        int green,
                        int blue,
                        String name) {
        check(red, green, blue);
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.name = name;
    }


    public int getRGB() {
        return ((red << 16) | (green << 8) | blue);
    }

    public String getName() {
        return name;
    }

    public ImmutableRGB invert() {
        return new ImmutableRGB(255 - red,
                       255 - green,
                       255 - blue,
                       "Inverse of " + name);
    }
}
分享到:
评论

相关推荐

    不可变对象ImmutableXXX:Collection、List、Set、Map…

    在Java编程中,不可变对象(Immutable Objects)是一种一旦创建后就不能被修改的对象。这种特性在多线程环境中尤其重要,因为不可变对象是线程安全的,它们不会因为并发修改而引发数据不一致的问题。Guava库是Google...

    Node.js-immutable-Javascript不可变的持久化数据集合

    `immutable.js`库为Node.js开发带来了不可变数据集合的便利,通过引入不可变性和持久化数据,提高了代码的稳定性和性能。结合函数式编程的理念,它为处理复杂状态和并发场景提供了一种强大而优雅的解决方案。在实际...

    不可变数据集合Immutable.js.zip

    Immutable 是 Facebook 开发的不可变数据集合。不可变数据一旦创建就不能被修改,是的应用开发更简单,允许使用函数式编程技术,比如惰性评估。Immutable JS 提供一个惰性 Sequence,允许高效的队列方法链,类似 map...

    kotlinx.collections.immutable,Kotlin的不可变集合原型.zip

    在Kotlin编程语言中,`kotlinx.collections.immutable`是一个重要的库,它提供了不可变集合的实现。不可变集合是一旦创建后就不能修改的集合,这种数据结构在多线程环境、函数式编程和构建安全的数据模型时非常有用...

    seamlessimmutable不可变JS数据结构向后兼容常规数组和对象

    Seamless Immutable是一款JavaScript库,它提供了一种创建不可变数据结构的方式,这些结构与JavaScript中的常规数组和对象保持向后兼容。不可变数据结构在编程中尤其在React和其他函数式编程库中变得越来越重要,...

    immutables,用于创建不可变对象和生成器的注释处理器。感觉像是番石榴的不可变集合,但对于常规值对象。包括json、jackson、gson、jax-rs、mongodb集成.zip

    Immutables是一个强大的Java库,专门用于创建不可变对象,并且提供了一个注解处理器来简化这一过程。在软件开发中,不可变对象是那些一旦创建就不能改变其状态的对象,这有助于提升程序的安全性、可预测性和并发性能...

    JAVA不可变类(immutable)机制与String的不可变性(推荐)

    Java中的不可变类(immutable)是一种特殊的类,其对象一旦创建后,其状态就不能再发生变化。这类类在Java中有着重要的地位,特别是String类,它是Java中最常用的不可变类之一。不可变类的设计旨在提高安全性、效率...

    为什么Java字符串是不可变对象?

     流行的Java面试题之一是:什么是不可变对象(immutable object),不可变对象有什么好处,在什么情况下应该用,或者更具体一些,Java的String类为什么要设成immutable类型?  不可变对象,顾名思义是创建后不可以...

    前端开源库-immutable-core

    1. **创建不可变数据**:提供`fromJS`方法将原始JavaScript对象转化为不可变对象。 2. **数据访问**:提供`get`、`getIn`方法安全地访问数据,即使在不存在的路径上也不会修改原对象。 3. **数据修改**:虽然数据...

    ImmutableObjects

    在Java编程语言中,不可变对象(Immutable Objects)是一个重要的概念,尤其是在构建健壮、易于维护的应用程序时。本篇将基于提供的文件内容来深入探讨不可变对象的概念及其在Java中的应用。 #### 不可变对象定义 ...

    21-C-ImmutableObject-Code.zip

    在编程领域,不可变对象(Immutable Object)是一种重要的设计模式,尤其在多线程和并发环境中,它提供了安全性和性能优化。在这个初学者教程中,我们将深入探讨C#中的不可变对象,包括“内部不变性”和“观察不变性...

    kotlinx.collections.immutable, Kotlin的不可变集合 Prototype.zip

    kotlinx.collections.immutable, Kotlin的不可变集合 Prototype Kotlin的不可变集合库 Kotlin的不可变集合接口和实现 Prototype 。有关详细信息,请参阅建议列表。Prototype实现基于 pcollections ( 版权 2015的作者...

    浅谈Python中的可变与不可变数据类型.pdf

    而不可变类型(Immutable types),指的是对象的内容一旦创建后就不能被改变,如果要修改,Python会重新在内存中创建一个新的对象来存储新的值,原对象保持不变。不可变类型包括整数(int)、浮点数(float)、字符...

    go-immutable-radix, 在Golang中,一个不可变的基数树实现.zip

    go-immutable-radix, 在Golang中,一个不可变的基数树实现 go-immutable-radix 提供实现不可变 radix的iradix 包。 包只提供单个 Tree 实现,针对稀疏节点优化。作为一个基数树,它提供以下内容:O(k) 操作。在许多...

    ImmutableObject:在 Javascript 中创建不可变对象

    #ImmutableObject ImmutableObject 被创建为一种简单的客户端 Javascript 方式来制作不可变对象。 它使用一组简单的方法来获取和设置项目。 仅当您为isExtensible传递true参数时才允许设置项目。 ##基本用法: 可以...

    dot-prop-immutable, 点prop的不可变版本,带有一些扩展名.zip

    dot-prop-immutable, 点prop的不可变版本,带有一些扩展名 dot-prop-immutable 点prop的不可变版本,带有一些扩展名。npm install dot-prop-immutable这个模块的动机是在不改变普通JavaScript对象的现有状态的情况

    immutable-object:如何构建和使用不可变对象的示例

    在编程领域,不可变对象(Immutable Object)是一种特殊类型的对象,一旦创建,其状态就不能被修改。这种设计模式在Java等编程语言中广泛应用于提高并发性能、保证数据安全以及简化编程逻辑。本篇文章将深入探讨如何...

    polyn-immutable:定义对象模式以进行验证以及构造不可变对象

    @ polyn / immutable使用本机JavaScript功能(即Object.freeze )使对象不可变。 它使用来验证您定义的架构,并且还支持自定义验证器(即,如果您更喜欢JSON架构)。 与Object.freeze不同,@ polyn / immutable递归...

Global site tag (gtag.js) - Google Analytics