用不变对象解决竞态问题

不可变对象
所有并发问题都是由于多个线程同时访问对象的某个可变属性引起的, 如果对象是不可变的, 那么所有的并发问题都将迎刃而解.
所谓不可变对象是指对象一旦构造完成, 其所有属性就不能更改, 不可变对象显然都是线程安全的.
对于不可变对象, 需要防止发生this逃逸.
如果需要对多个成员进行一项原子操作, 可以考虑使用这些成员构建一个不可变类. 例如:
Java代码
用不变对象解决竞态问题
文章图片
【用不变对象解决竞态问题】publicclassCashedClass {
privateString cashedStr ="";
privateintcashedHashCode;


publicinthashCode(String str) {
// 如果str是cashedStr, 就直接返回缓存的hashCode值
if(str.equals(cashedStr)) {
returncashedHashCode;
}else{
// 将cashedStr和hashCode值缓存起来
cashedStr = str;
cashedHashCode = cashedStr.hashCode();
returncashedHashCode;
}
}
}
CashedClass不是一个线程安全的类, 因为对cashedStr和cashedHashCode的读写操作不具备原子性, 会发生race condition. 除了使用synchronized进行同步之外, 我们还可以使用不可变对象消除race condition:
Java代码
用不变对象解决竞态问题
文章图片
publicclassCashedClass {
// 使用一个volatile变量持有OneCashedValue对象
privatevolatileOneCashedValue oneValue =https://www.it610.com/article/newOneCashedValue("",0);


publicinthashCode(String str) {
inthashCode = oneValue.getStrHashCode(str);
if(hashCode == -1) {
hashCode = str.hashCode();
// 对volatile变量的修改不依赖于当前值, 符合volatile的使用场景
oneValue =https://www.it610.com/article/newOneCashedValue(str, hashCode);
}
returnhashCode;
}


/**
* 这是一个不可变类
*/
publicclassOneCashedValue {
// 成员变量都是final的
privatefinalString str;
privatefinalintstrHashCode;


// 构造过程中不会发生this逃逸
publicOneCashedValue(String str,intstrHashCode) {
this.str = str;
this.strHashCode = strHashCode;
}


publicintgetStrHashCode(String str) {
if(!this.str.equals(str)) {
// -1表示无效的hashCode值
return-1;
}
returnstrHashCode;
}
}
}

    推荐阅读