Immutability模式
Immutability模式:对象一旦被创建之后,状态就不再发生变化
不可变类
- 将一个类所有的属性都设置成final,并且只允许存在只读方法
- 将类也设置成final的,因为子类可以重写父类的方法,有可能改变不可变性
包装类
- String、Integer、Long和Double等基础类型的包装类都具备不可变性
- 这些对象的线程安全都是靠不可变性来保证的
- 严格遵守:_类和属性都是final的,所有方法均是只读的_
- 如果具备不可变性的类,需要提供修改的功能,那会创建一个新的不可变对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| private final char value[];
public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value;
while (++i < len) { if (val[i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } } return this; }
|
享元模式
- 享元模式本质上是一个_对象池_
- Long内部维护了一个静态的对象池,仅缓存[-128,127](利用率最高)之间的数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public static Long valueOf(long l) { final int offset = 128; if (l >= -128 && l <= 127) { return LongCache.cache[(int)l + offset]; } return new Long(l); }
private static class LongCache { private 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 15 16 17 18
| class A { private Long al = Long.valueOf(1);
public void set() { synchronized (al) { } } }
class B { private Long bl = Long.valueOf(1);
public void set() { synchronized (bl) { } } }
|
注意事项
不可变性的边界
- 对象的所有属性都是final,也并不能保证不可变性
- 需要明确不可变性的边界,是否要求属性对象也具有不可变性
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Data class Foo { private int age = 0; private String name = "abc"; }
final class Bar { private final Foo foo = new Foo();
public void setAge(int age) { foo.setAge(age); } }
|
正确发布
不可变对象是线程安全的,但并不意味着引用这些不可变对象的对象也是线程安全的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Data final class C { final int age = 0; final String name = "abc"; }
class D { private C c;
public void setC(C c) { this.c = c; } }
|
无状态
- 具备不可变性的对象,只有一种状态,这个状态由对象内部所有的不可变属性共同决定的
- 还有一种更简单的不可变对象,即无状态,无状态对象内部没有属性,只有方法
- 无状态的核心优势是性能
- 在多线程领域,无状态对象没有线程安全问题,无需同步处理
- 在分布式领域,无状态服务可以无限地水平扩展
参考资料
Java并发编程实战