同步块内或者不可变对象的引用变量必须是“本类的”,不能指向外部对象引用,也不能向外部发布对象引用。
《Java Concurrency in Practice》中有两个例子:
《Java Concurrency in Practice》中有两个例子:
1. 例子1:
public class CachedFactorizer implements Servlets {
@GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
@GuardedBy("this") private long this;
@GuardedBy("this") private long cacheHist;
public synchronized long getHits() {return hist;}
public synchronized double getCacheHitRatio(){
return (double)cacheHits / (double)hists;
}
public void service(ServletRequest req, ServletResponse resp){
BigInterger i = extractFromRequest(req);
BigInteger[] factors = null;
synchronized(this){
++hits;
if(i.equls(lastNumber)){
++cachedHits;
factors = lastFactors.clone();
}
}
if(factors == null){
factos = factor(i);
synchronized(this){
lastNumbe = i;
lastFactors = factors.clone();
}
}
encodeIntroResponse(resp,factors);
}
}
public class CachedFactorizer implements Servlets {
@GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
@GuardedBy("this") private long this;
@GuardedBy("this") private long cacheHist;
public synchronized long getHits() {return hist;}
public synchronized double getCacheHitRatio(){
return (double)cacheHits / (double)hists;
}
public void service(ServletRequest req, ServletResponse resp){
BigInterger i = extractFromRequest(req);
BigInteger[] factors = null;
synchronized(this){
++hits;
if(i.equls(lastNumber)){
++cachedHits;
factors = lastFactors.clone();
}
}
if(factors == null){
factos = factor(i);
synchronized(this){
lastNumbe = i;
lastFactors = factors.clone();
}
}
encodeIntroResponse(resp,factors);
}
}
如果将"factors = lastFactors.clone();"改为“factors = lastFactors;”,将会出现错误。假设一个线程执行factors=lastFactors;而这时另一个线程执行lastFactors = factors.clone(); 这时就是一个线程读lastFactors,而另个线程写lastFactors,就是在不同线程之间共享可变变量,但是读线程是在自己的代码内读lastFactors的值,那么每个线程获取lastFactors后,都要在CachedFactorizer对象上实现同步,不是一种好办法。lastFactors变量不能发布出去,所以就让factors等于lastFactors的一个克隆对象。每个读线程就获取不同的对象。
2.例子2
不可变对象提供一种弱形式的原子性。每当需要对一组相关数据以原子性的方式执行某个操作时,就可以考虑创建一个不可变的类来包含这些数据。同样要保证类里面包含的数据都是“本类的”,就是所有引用未指向外部引用,已经所有数据不能被外部修改。下面这个类就不是线程安全的。
class OneValueCache {
private final BigInteger lastNumer;
private final BigInterger[] lastFactors;
public OneValueCahche(BigInterger i, BigInteger[] factors){
lastNumber = i;
lastFactors = factors;
}
public BigInteger[] getFactors(BigInteger i){
if(lastNumber == null || !lastNumber.equals(i)){
return null;
}else{
return Arrays.copyOf(lastFactors,lastFactors.length);
}
}
}
因为lastFactors这个引用指向外部,当外部变化时,它也跟着变化,这就不能保证lastFactors还是保存的lastNumber的因数。应该使用拷贝或者克隆,产生一个新的属于类的引用。
class OneValueCache {
private final BigInteger lastNumer;
private final BigInterger[] lastFactors;
public OneValueCahche(BigInterger i, BigInteger[] factors){
lastNumber = i;
lastFactors = Arrays.copyOf(factors,factors.length);//or lastFactors = factors.clone();
}
public BigInteger[] getFactors(BigInteger i){
if(lastNumber == null || !lastNumber.equals(i)){
return null;
}else{
return Arrays.copyOf(lastFactors,lastFactors.length);
}
}
}
class OneValueCache {
private final BigInteger lastNumer;
private final BigInterger[] lastFactors;
public OneValueCahche(BigInterger i, BigInteger[] factors){
lastNumber = i;
lastFactors = factors;
}
public BigInteger[] getFactors(BigInteger i){
if(lastNumber == null || !lastNumber.equals(i)){
return null;
}else{
return Arrays.copyOf(lastFactors,lastFactors.length);
}
}
}
因为lastFactors这个引用指向外部,当外部变化时,它也跟着变化,这就不能保证lastFactors还是保存的lastNumber的因数。应该使用拷贝或者克隆,产生一个新的属于类的引用。
class OneValueCache {
private final BigInteger lastNumer;
private final BigInterger[] lastFactors;
public OneValueCahche(BigInterger i, BigInteger[] factors){
lastNumber = i;
lastFactors = Arrays.copyOf(factors,factors.length);//or lastFactors = factors.clone();
}
public BigInteger[] getFactors(BigInteger i){
if(lastNumber == null || !lastNumber.equals(i)){
return null;
}else{
return Arrays.copyOf(lastFactors,lastFactors.length);
}
}
}