3.4. Immutability(不变性)

3.4. Immutability(不变性)
The other end-run around the need to synchronize is to use immutable objects [EJ Item 13]. Nearly all the atomicity and visibility hazards we've described so far, such as seeing stale values, losing updates, or observing an object to be in an inconsistent state, have to do with the vagaries of multiple threads trying to access the same mutable state at the same time. If an object's state cannot be modified, these risks and complexities simply go away.
为了避免使用同步机制的另外一种方式是使用不可变对象。迄今为止我们描述过的所有原子性和可见性隐患(例如过期数据、update丢失、或者看到一个对象的非稳定状态),都是由于多个线程在同一时间访问相同的可变状态造成的。但是如果对象的状态是不可修改的,那么这些危险和复杂性就全部消除了。
An immutable object is one whose state cannot be changed after construction. Immutable objects are inherently thread-safe; their invariants are established by the constructor, and if their state cannot be changed, these invariants always hold.
immutable对象是指该对象的状态在被构造后就是不可变的。Immutable对象在本质上是线程安全的,这种不变性由其构造器所建立,并且这种不变性将会一直保持。
Immutable objects are always thread-safe.
不可变的对象永远是线程安全的。

Immutable objects are simple. They can only be in one state, which is carefully controlled by the constructor. One of the most difficult elements of program design is reasoning about the possible states of complex objects. Reasoning about the state of immutable objects, on the other hand, is trivial.
Immutable对象非常简单。它们可能只有一个状态,而该状态由其构造器所控制。在程序设计过程中一个非常困难的点是搞清楚复杂对象的可能状态。但是,思考immutable对象的状态是没有意义的。
Immutable objects are also safer. Passing a mutable object to untrusted code, or otherwise publishing it where untrusted code could find it, is dangerous the untrusted code might modify its state, or, worse, retain a reference to it and modify its state later from another thread. On the other hand, immutable objects cannot be subverted in this manner by malicious or buggy code, so they are safe to share and publish freely without the need to make defensive copies [EJ Item 24].
Immutable对象也是安全的。将可变对象传递给不可信赖的代码或者使得不可信赖的号码可以访问到可变对象的时候是很危险的,因为这些不可信赖的代码可能会修改可变对象的状态,还有更可怕的情况:通过保留一个对该对象应用使得后续的线程可能会修改该对象。但是,immutable对象并不存在被恶意的、错误的方式修改的可能。因此这样的对象可以自由的被共享或者公开,而不需要做defensive复制。
Neither the Java Language Specification nor the Java Memory Model formally defines immutability, but immutability is not equivalent to simply declaring all fields of an object final. An object whose fields are all final may still be mutable, since final fields can hold references to mutable objects.
无论是Java语言规范和JMM都没有针对immutability给出一个明确的定义。但是immutability并不是简单的将所有的对象域都声明为final类型的。一个所有域都是final的对象仍然可能是可变的,因为fianal域可能包含对可变对象的应用。
An object is immutable if:
• Its state cannot be modifled after construction;
• All its flelds are final;[12] and
• It is properly constructed (the this reference does not escape during construction).
所有的状态在构造后无法被修改。
所有的域都是final的。
被良好的构造(this应用在构造的过程中不会逃逸)

[12] It is technically possible to have an immutable object without all fields being final。 String is such a class but this relies on delicate reasoning about benign data races that requires a deep understanding of the Java Memory Model. (For the curious: String lazily computes the hash code the first time hashCode is called and caches it in a nonfinal field, but this works only because that field can take on only one nondefault value that is the same every time it is computed because it is derived deterministically from immutable state. Don't try this at home.)
在不是所有域都是final的情况下构造一个immutable对象同样是可行的。String类就是这样一个类,但是这会依赖于对数据竞争的良好理解,这种理解需要对java内存模型的深入研究。(String将会在hashcode第一次被调用的时候延迟计算hash code,然后将其缓存在一个非final域中。这种做法能够实现仅仅是因为该域只有在一个非默认的的值在每次计算都相同的情况下才会有价值,因为该值是由追溯其不可变状态而获得的,不要随便使用这种模式)
Immutable objects can still use mutable objects internally to manage their state, as illustrated by ThreeStooges in Listing 3.11. While the Set that stores the names is mutable, the design of ThreeStooges makes it impossible to modify that Set after construction. The stooges reference is final, so all object state is reached through a final field. The last requirement, proper construction, is easily met since the constructor does nothing that would cause the this reference to become accessible to code other than the constructor and its caller.
Immutable对象中仍然可能在其内部包含可变对象来管理其状态。尽管保存名字的Set是可变的,ThreeStooges类的设计使得在其被构建之后,Set就不可能会被修改。Stooges的应用是final的,这样一来,所有的对象状态只能通过一个final域来访问的要求就实现了。最后一点,良好的构建也已经被实现了,构造器并没有将this应用公开给其他代码。
Listing 3.11. Immutable Class Built Out of Mutable Underlying Objects.
@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);
    }
}

Because program state changes all the time, you might be tempted to think that immutable objects are of limited use, but this is not the case. There is a difference between an object being immutable and the reference to it being immutable. Program state stored in immutable objects can still be updated by "replacing" immutable objects with a new instance holding new state; the next section offers an example of this technique.[13]
[13] Many developers fear that this approach will create performance problems, but these fears are usually unwarranted. Allocation is cheaper than you might think, and immutable objects offer additional performance advantages such as reduced need for locking or defensive copies and reduced impact on generational garbage collection.
由于代码的状态一直在不断的改变,你可能会认为immutable对象必须被有限度的使用,事实并非如此。一个对象时immutable的和对该对象的应用是immutable是由区别的。保存在immutable对象中的程序状态仍然有可能通过更改一个保存新状态的immutable对象而被修改。下一节中将会展现该技术。
很多开发者害怕这种做法可能会导致性能问题,但是这种担心是没有依据的。对对象的分配比你想象的代价更低,而且immutable对象可以提供额外的性能优势例如减少对锁的使用以及defensive copy。并且可以减少连锁的垃圾回收机制的冲击。
3.4.1. Final Fields(final域)
The final keyword, a more limited version of the const mechanism from C++, supports the construction of immutable objects. Final fields can't be modified (although the objects they refer to can be modified if they are mutable), but they also have special semantics under the Java Memory Model. It is the use of final fields that makes possible the guarantee of initialization safety (see Section 3.5.2) that lets immutable objects be freely accessed and shared without synchronization.
Final关键字是C++中常量机制的一种受限版本,Final关键字可以用来支持immutable对象的构造。Final域是不能被修改的(虽然他所引用的对象可能被修改),但是final域在java内存模型下有其独特的语法。Final域的用处就在于对初始化安全性的保证成为可能,这种可能性使得immutable对象在无需同步机制的情况下可以被安全的访问和共享。
Even if an object is mutable, making some fields final can still simplify reasoning about its state, since limiting the mutability of an object restricts its set of possible states. An object that is "mostly immutable" but has one or two mutable state variables is still simpler than one that has many mutable variables. Declaring fields final also documents to maintainers that these fields are not expected to change.
即使某个对象是可变的,使得其某些域变成final的也可以简化对其状态的管理,因为限制对象的可变性就意味着限制了对象状态的集合。一个“大部分不可变”只有一个或两个可变状态的变量的对象仍然比含有很多可变变化的对象简单。将某个域声明为final也同样可以告诉该对象的维护者应该不要将该域修改。
Just as it is a good practice to make all fields private unless they need greater visibility [EJ Item 12], it is a good practice to make all fields final unless they need to be mutable.
在不需要更高可见性的情况下,将所有的域都变成是private是一个很好的实践,除非你想获得可变性,将所有域都变成final是一个很好的实践。


3.4.2. Example: Using Volatile to Publish Immutable Objects(使用volatile公开Immutable对象)
In UnsafeCachingFactorizer on page 24,we tried to use two AtomicReferences to store the last number and last factors, but this was not thread-safe because we could not fetch or update the two related values atomically. Using volatile variables for these values would not be thread-safe for the same reason. However, immutable objects can sometimes provide a weak form of atomicity.
在UnsafeCachingFactorizer类中,我们曾经尝试使用两个AtomicReferences来保存最新的数值和最新的因子,但这不是线程安全的,因为我们无法通过原子的获取或修改这两个值。同样的原因,使用对这些值使用volatile类型的变量同样是不安全的,但是Immutable对象可以提供一种弱形式的原子性。
The factoring servlet performs two operations that must be atomic: updating the cached result and conditionally fetching the cached factors if the cached number matches the requested number. Whenever a group of related data items must be acted on atomically, consider creating an immutable holder class for them, such as OneValueCache[14] in Listing 3.12.
[14] OneValueCache wouldn't be immutable without the copyOf calls in the constructor and getter. Arrays.copyOf was added as a convenience in Java 6; clone would also work.
因式分解servlet执行的两个操作必须是原子的,修改缓存值,如果缓存的值命中请求值的话获得缓存的因数。无论如何一组相关的数据项必须被原子性的起作用。可以考虑创建一个Immutable的对象来保持他们例如OnValueCache。
Race conditions in accessing or updating multiple related variables can be eliminated by using an immutable object to hold all the variables. With a mutable holder object, you would have to use locking to ensure atomicity; with an immutable one, once a thread acquires a reference to it, it need never worry about another thread modifying its state. If the variables are to be updated, a new holder object is created, but any threads working with the previous holder still see it in a consistent state.
存在于对多个相关变量的访问和升级场景中的条件竞争可能通过使用Immutable对象来保存这些变量来缓解。在一个可变对象中保持变量,你必须使用锁机制来保证原子性,在Immutable中,即使线程获得了对该对象的应用,你也不必担心其他线程可能会修改这种状态。如果变量需要被修改,一个新的保存对象需要被创建,但是所有其他使用上一个保存对象的线程仍然可以看到一致的状态。
Listing 3.12. Immutable Holder for Caching a Number and its Factors.
@Immutable
class OneValueCache {
    private final BigInteger lastNumber;
    private final BigInteger[] lastFactors;

    public OneValueCache(BigInteger i,
                         BigInteger[] factors) {
        lastNumber  = i;
        lastFactors = Arrays.copyOf(factors, factors.length);
    }

    public BigInteger[] getFactors(BigInteger i) {
        if (lastNumber == null || !lastNumber.equals(i))
            return null;
        else
            return Arrays.copyOf(lastFactors, lastFactors.length);
    }
}

VolatileCachedFactorizer in Listing 3.13 uses a OneValueCache to store the cached number and factors. When a thread sets the volatile cache field to reference a new OneValueCache, the new cached data becomes immediately visible to other threads.
Listing3.13中的VolatileCachedFactorizer对象使用OneValueCache来保存被缓存的值和因数。当一个线程将volatile缓存与设置为一个新的OneValueCache,新的缓存数据就会立刻变得对其他线程可变。
The cache-related operations cannot interfere with each other because One-ValueCache is immutable and the cache field is accessed only once in each of the relevant code paths. This combination of an immutable holder object for multiple state variables related by an invariant, and a volatile reference used to ensure its timely visibility, allows VolatileCachedFactorizer to be thread-safe even though it does no explicit locking.
缓存相关的操作并不会互相影响,因为OneValueCache是Immutable而且缓存域每次只能使用一条路径来访问它。使用Immutable保存对象来保证多个相关状态变量的一致性;使用volatile引用来确保其时间可见性的组合使得VolatileCachedFactorizer类在没有使用到显性锁的情况下,依然能够保证线程安全。

你可能感兴趣的:(设计模式,thread,cache,servlet,performance)