Immutable Object模式
(一)Immutable Object模式
在多线程环境中,一个对象通常会被多的线程共享,如果存在多个线程并发地修改该对象的状态或者一个线程访问该对象的状态而其他线程想要修改该对象的状态,就会出错。因此通常情况下,我们就会对这个对象加锁。加锁显然会有额外的开销,因此,我们可以使用Immutable Object模式來代替加锁的效果。
Immutable Object模式通过使用内容对外可见,但是状态不可变的对象使得被共享对象天生线程安全。
(二)举个��
假设我们用一个数组存储了一个同学的成绩。
Arraylist gradle
现在有两个线程,一个线程更新每一个学期,该同学的成绩,一个线程则是获取该同学的平均分。如果不加锁,可能在更新成绩到一半的时候,另一个线程则过来计算平均分,这时gradle中有的科目是旧的成绩,有的是新的成绩,这时就会得出错误的平均分。
解决方式:Immuatable Object模式
public final class gradleList{
private volatitle Arraylist gradle;
public gradleList(){
获取成绩 ,赋值list
}
public static list getGradle() {
return gradle;
}
}
public class student {
private gradlist list;
public void setList(gradlist list) {
this.list = list;
}
public gradlist getList() {
return list;
}
}
其中gradleList是不可变对象,只有get方法。这就决定了每次gradleList都是new出来的,而不是改变其中的变量获得的。
(三)使用场景
Immutable Object方法每次更新都要new一个不可变量出来,因此如果更新太频繁,那么资源耗用太大,不可取。在上个例子中,学生的成绩每个学期更新一次,所以比较适用。此外HashMap的key值变化会导致hashcode变化,因此我们通常使用string作为key值。这也是一种不可变对象。使用不可变对象作为key值是非常好的。
(四)类图
(五)源码
CopyOnWriteArrayList
CopyOnWriteArrayList是线程安全的,它就是使用了不可变对象模式。
public synchronized void add(int index, E e) {
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, index);
newElements[index] = e;
System.arraycopy(elements, index, newElements, index + 1, elements.length - index);
elements = newElements;
}
这里的synchronized 用于修饰add方法,避免同时添加。而其中存储的对象则是使用了不可变对象模式,每次add时会新建newElements并将所有数据存进去,新加的对象存在最后一个。因此
CopyOnWriteArrayList消耗很大,不适合频繁使用。