Immutable意为不可改变的,如果一个对象定义成了不可变的(即Immutable Object),就意味着该对象在初始化完成之后它的属性是不能够被修改的。在并发编程中我们可以将对象设计成Immutable Object从而不用加锁实现线程安全,因为不可变对象一定是线程安全的,同时由于不需要用一些锁机制等保证内存一致性问题也减少了同步开销。
谈到Immutable Object会让很多Javaer联想到Java语言中的final关键字,final关键字可以修饰属性、方法和类。final的作用为
final 修饰的 class 代表该类不可以继承扩展,
final 的变量是不可以修改的,
而 final 的方法也是不可以重写的(override)。
如何定义Immutable Object
Java 语言目前还没有原生的不可变对象的支持,但在Java™ Tutorials中给出的如何定义一个不可变对象的方法。
- 类中的属性不提供"setter"方法;
- 类中所有的属性声明成private和final类型;
- 类也声明成final的,以防止类被继承;
- 如果有属性是引用类型的,也要防止引用类型的属性被调用方修改了,如通过构造器初始化所有成员,尤其是引用对象要进行深拷贝(deep copy,符合copy-on-write 原则);
- 如果确实需要实现 getter 方法,或者其他可能会返回内部状态的方法,也要深拷贝,创建私有的 copy。
代码样例:
package com.zl.step18;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
// 不可变对象不允许被继承
public final class IntegerAccumulator {
// 不可变 变量不可被修改
private final int init ;
public IntegerAccumulator(int init){
this.init = init ;
}
public IntegerAccumulator(IntegerAccumulator accumulator ,int init){
this.init = accumulator.getValue()+init ;
}
public IntegerAccumulator add(int i){
return new IntegerAccumulator(this, i) ;
}
public int getValue() {
return this.init ;
}
public static void main(String[] args) {
IntegerAccumulator accumulator = new IntegerAccumulator(0) ;
IntStream.range(0,3).forEach(i-> new Thread(()-> {
int inc = 0 ;
while (true) {
int oldValue = accumulator.getValue() ;
int result = accumulator.add(inc).getValue() ;
System.out.println(oldValue+" + "+ inc + " = " + result );
if(inc + oldValue != result){
System.err.println("Error: " + oldValue + " + " +inc + " = " + result);
}
inc ++ ;
slowly();
}
}).start());
}
private static void slowly() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
优点:
- 1、因为是不可变的,所以不允许程序对其进行修改,避免了程序中修改数据带来的异常产生
- 2、由于对象是不可变的,减少了线程同步带来的开销
缺点:
- 1、每次返回都创建新的对象,内存会有一定的开销,不容易被垃圾回收器回收,造成资源的浪费
应用场景:
- 1、不适合大对象、且创建频繁的场景,因为对象大且创建频繁会容易导致内存泄漏
- 2、适合表示抽象数据类型(如数字、枚举类型或颜色)的值
- 3、适合在多线程环境中进行同步而不需要考虑线程同步