在并行软件的开发过程中,同步操作似乎是不可避免的,当多线程对同一个对象进行读写操作时,为了保证数据一致性和正确性,有必要对对象进行同步。而同步操作对系统的性能是有相当的损耗的,为了尽可能的取出这些同步操作,提高程序并行能力,可以使用一种不可变对象,依靠对象的不变性
可以确保其在没有同步操作的多线程环境中依然时钟保持内部状态一致性和正确性,这就是不变模式。
不变模式天生就是多线程友好的,它的核心思想是,一个对象一旦被创建,则它的状态将永远不会发生改变。所以,没有一个线程可以修改其内部状态和数据,同时其内部状态也绝不会自行发生改变。基于这些特性,对不变对象的多线程操作不需要进行同步控制。
不变模式和只读模式是有一定区别的。不变模式是比只读属性具有更强一致性和不变性。对只读属性的对象而言,对象本身不能被其他线程修改,但是对象的自身状态却可能自行修改。
不变模式的主要使用场景需要满足2个条件:
1. 当前对象创建后,其内部状态和数据不再发生任何变化。
2. 对象需要被共享,被多线程频繁访问。
在Java语言中,不变模式的实现很简单。为确保对象被创建后,不发生任何改变,并保证不变模式正常工作,只需注意以下4点:
1. 取出所有setter方法及所有修改自身属性的方法.。
2. 将所有属性设置为私有,并用final修饰,确保其不可修改。
3. 确保没有子类可以重载修改它的行为,即final class。
4. 有一个可以创建完整对象的构造函数。
以下代码实现了一个不变的产品对象,它拥有序列号、名称和价格三个属性。
public final class Product {
private final String no;
private final String name;
private final double price;
public Product(String no, String name, double price) {
super();
this.no = no;
this.name = name;
this.price = price;
}
public String getNo() {
return no;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
在不变模式的实现中,final关键字起到了重要的作用。对属性的final定义确保所有数据只能在对象被构造时赋值1次。之后,就永远不再发生变化。而对class的final确保了类不会有子类。
在JDK中,不变模式的应用非常广泛。其中,最为典型的就是java.lang.String类。此外,所有的元数据类包装类,都是使用不变模式实现的。主要的不变模式类型如下:
java.lang.String
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Short
由于基本数据类型和String类型在实际的软件开发中应用及其广泛,使用不变模式后,所有实例的方法均不需要进行同步操作,保证了它们在多线程环境下的性能。
注:本篇博客内容摘自《Java高并发程序设计》