一、定义:
不变模式:为了能尽可能地去除那些同步操作,提高并行程序性能,可以使用一种不可改变的对象,依靠对象的不变性,可以确保其在没有同步操作的多线程环境中依然始终保持内部状态的一致性和正确性。
不变模式天生就是多线程友好的,它的核心思想是,一个对象一旦被创建,则它的内部状态将永远不会发生改变。所以,没有一个线程可以修改其内部状态和数据,同时其内部状态也绝不会自行发生改变。基于这些特性,对不变对象的多线程操作不需要进行同步控制。
不变模式和只读属性是有一定的区别的。不变模式是比只读属性具有更强的一致性和不变性。对只读属性的对象而言,对象本身不能被其他线程修改,但是对象的自身状态却可能自行修改。
比如,一个对象的存活时间(对象创建时间和当前时间的时间差)是只读的,因为任何一个第三方线程都不能修改这个属性,但是这是一个可变的属性,因为随着时间的推移,存活时间时刻都在发生变化。而不变模式则要求,无论出于什么原因,对象自创建后,其内部状态和数据保持绝对的稳定。
注意:不变模式通过回避问题而不是解决问题的态度来处理多线程并发访问控制。不变对象是不需要进行同步操作的。由于并发同步会对性能产生不良的影响,因此,在需求允许的情况下,不变模式可以提高系统的并发性能和并发量。
三、不变模式的实现需要注意4点:
1.去除setter方法以及所有修改自身属性的方法。
2.将所有属性设置为私有,并用final标记,确保其不可修改。
3.确保没有子类可以重载修改它的行为。
4.有一个可以创建完整对象的构造函数。
如:
public final class Product
{ //确保无子类
private final String no; //私有属性,不会被其他对象获取
private final String name; //final保证属性不会被2次赋值
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中不变模式的应用
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类型在实际的软件开发中应用极其广泛,使用不变模式后,所有实例的方法均不需要进行同步操作,保证了它们在多线程环境下的性能。