Immutability模式:如何利用不变性解决并发问题?

并发情况下如果对一个共享变量进行读写就会有并发问题,如果只有读是没有并发问题的,所以解决并发问题的一个重要思路就是不提供变量的写的功能

如何快速实现不可变类

  1. 类的声明以及属性都要声明成final对象(类的声明是为了防止子类继承父类修改父类的属性)
  2. 只提供只读的方法,如果要修改对象,去创建一个新的不可变对象。

例如String,是典型的一个不可变对象,String中的replace对象在修改时候我们是创建了一个新的不可变对象

利用flyweight模式减少重复对象的创建

由于不可变对象不能修改,如果遇到修改话可能会产生大量的对象占用系统内存,为了避免这种情况发生JAVA采用了flyweight模式来减少对象的产生,用一句话概述

提前缓存好一部分对象,如果没有在去创建,如果有就直接取用,因为这些对象是不可变的共享完全没问题。
比较特殊的是Long,因为对象范围比较大,所以Long只保存了-128-127的对象。具体见代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Long valueOf(long l) {
final int offset = 128;
// [-128,127] 直接的数字做了缓存
if (l >= -128 && l <= 127) {
return LongCache
.cache[(int)l + offset];
}
return new Long(l);
}
// 缓存,等价于对象池
// 仅缓存 [-128,127] 直接的数字
static class LongCache {
static final Long cache[]= new Long[-(-128) + 127 + 1];

static {
for(int i=0; i<cache.length; i++)
cache[i] = new Long(i-128);
}
}

注意事项

不可变对象的属性即时声明了不可变,但是他的属性也有可能改变如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Foo 线程安全
class Foo {
int age = 0;
String name = "abc";
}

final class Bar {
final Foo foo=new Foo();//可改变foo里的属性
final int[] i=new int[10];//可改变数组元素

public void setAge(int i){
foo.age=i;
}
}

不可变类作为属性本身不具备不可见性并且是线程不安全的

1
2
3
4
5
6
7
8
9
10
11
12
//Foo 线程安全
final class Foo{
final int age=0;
final int name="abc";
}
//Bar 线程不安全
class Bar {
Foo foo;//并发时候会有问题
void setFoo(Foo f){
this.foo=f;
}
}