第三部分-并发设计模式28:Immutability
1.什么是Immutability
Immutability,不变性,
叫做不变性设计模式,简单来说就是对象一旦创建,状态就不再发生变化。
变量一旦被赋值,就不允许修改了(没有写操作);没有修改操作,就保持了不变性
2.什么情况用Immutablity设计模式
多个线程同时读写同一个共享变量存在并发问题,核心条件是读写,如果只有读,没有写,是没有并发问题的
解决并发问题,最简单的办法就是让共享变量只有读操作,没有写操作。
3.快速实现具备不可变的类
将一个雷的所有属性都设置为final,并且只允许存在只读方法,那么这个类基本上就具备不可变性了。并且这个类本身也是final的,这个类本身不允许继承。
4.jdk中使用Immutablity设计模式的,不可变的类
Long,String
public final class String {
private final char value[];
// 字符替换
String replace(char oldChar,
char newChar) {
//无需替换,直接返回this
if (oldChar == newChar){
return this;
}
int len = value.length;
int i = -1;
/* avoid getfield opcode */
char[] val = value;
//定位到需要替换的字符位置
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
//未找到oldChar,无需替换
if (i >= len) {
return this;
}
//创建一个buf[],这是关键
//用来保存替换后的字符串
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);
}
}
String中的可变方法,往往是新new 出一个新不可变对象来实现,会不会浪费内存呢?会的
5.如何解决内存浪费,使用享元模式
Long,Integer,Short,Byte 等这些基本数据类型的包装类都使用了享元模式
Long使用享元模式的范例(本质上是一个对象池或者缓存池的概念)
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);
}
}
String,Integer类也是类似
6.使用享元模式的这些包装类为什么不适合用于锁?
是因为有些看起来像是私有的锁,其实是共有的
伪代码,看着像是两把锁,其实是同一把锁
class A {
Long al=Long.valueOf(1);
public void setAX(){
synchronized (al) {
//省略代码无数
}
}
}
class B {
Long bl=Long.valueOf(1);
public void setBY(){
synchronized (bl) {
//省略代码无数
}
}
}
7.Immutability模式注意事项
- 对象的所有属性都是final的,并不能保证不可变性
- 不可变对象也需要正确发布
final修饰的普通对象,对象属性是可以被修改的
8.正确使用Immutability的姿势
public final class ServerConfig {
private final int masterExecThreads = 100;
private final int masterExecTaskNum = 20;
private final int listenPort = 5678;
private final String address = NetUtils.getAddr(listenPort);
public String getAddress() {
return address;
}
public int getMasterExecThreads() {
return masterExecThreads;
}
public int getMasterExecTaskNum() {
return masterExecTaskNum;
}
public int getListenPort() {
return listenPort;
}
}
原创:做时间的朋友