@ThreadSafe
public class StatelessFactorizer implements Servlet {
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
encodeIntoResponse(resp, factors);
}
}
没有状态的对象是线程安全的。因为以上的代码是线程安全的。
Stateless objects are always thread-safe.
@NotThreadSafe
public class UnsafeCountingFactorizer implements Servlet {
private long count = 0;
public long getCount() { return count; }
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
++count;
encodeIntoResponse(resp, factors);
}
}
UnsafeCountingFactorizer has several race conditions that make its results unreliable.这里针对count的++操作其实是一个复合操作,完整包含了(read-modify-write)读取-修改-写入操作。
Operations A and B are atomic with respect to each other if, from the perspective of a thread executing A, when another thread executes B, either all of B has executed or none of it has. An atomic operation is one that is atomic with respect to all operations, including itself, that operate on the same state.
@ThreadSafe
public class CountingFactorizer implements Servlet {
private final AtomicLong count = new AtomicLong(0);
public long getCount() { return count.get(); }
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
count.incrementAndGet();
encodeIntoResponse(resp, factors);
}
}
在可行的情况下,使用现有的线程安全对象(如 AtomicLong)来管理类的状态。 推理现有线程安全对象的可能状态和状态转换比推理任意状态变量更简单,这使得维护和验证线程安全变得更容易。
但是并不是使用了线程安全对象就一定是线程安全的。比如
@NotThreadSafe
public class UnsafeCachingFactorizer implements Servlet {
private final AtomicReference<BigInteger> lastNumber = new AtomicReference<BigInteger>();
private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<BigInteger[]>();
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber.get())) {
encodeIntoResponse(resp, lastFactors.get());
} else {
BigInteger[] factors = factor(i);
lastNumber.set(i);
lastFactors.set(factors);
encodeIntoResponse(resp, factors);
}
}
}
因为这里是有两个状态的,针对单个lastNumber和单个lastFactors的操作是原子的,但是同时针对lastNumber进行判断然后再读取lastFactors的值其实是一个复合操作。所以以上代码并不是线程安全的。
@ThreadSafe
public class SynchronizedFactorizer implements Servlet {
@GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
public synchronized void service(ServletRequest req,
ServletResponse resp) {
BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber))
encodeIntoResponse(resp, lastFactors);
else {
BigInteger[] factors = factor(i);
lastNumber = i;
lastFactors = factors;
encodeIntoResponse(resp, factors);
}
}
}
在以上代码当中,通过一个内置锁来保证整个方法代码的同步。因此是线程安全的。但是以上代码性能很差,因为所有的代码都进行了同步。
Servlet that caches last result, but with unnacceptably poor concurrency. Don’t do this.
@ThreadSafe
public class CachedFactorizer implements Servlet {
@GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
@GuardedBy("this") private long hits;
@GuardedBy("this") private long cacheHits;
public synchronized long getHits() { return hits; }
public synchronized double getCacheHitRatio() {
return (double) cacheHits / (double) hits;
}
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = null;
synchronized (this) {
++hits;
if (i.equals(lastNumber)) {
++cacheHits;
factors = lastFactors.clone();
}
}
if (factors == null) {
factors = factor(i);
synchronized (this) {
lastNumber = i;
lastFactors = factors.clone();
}
}
encodeIntoResponse(resp, factors);
}
}
对于可能被多个线程访问的每个可变状态变量,对该变量的所有访问都必须在持有相同的锁的情况下执行。 在这种情况下,我们说变量被那个锁保护着。每个共享的可变变量都应该由一个锁保护。 让维护人员清楚地知道那是哪个锁。
简单性和性能之间经常存在矛盾。 在实施同步策略时,请抵制为了性能而过早牺牲简单性(可能会损害安全性)的诱惑。如果需要优化的话,避免在长时间计算或可能无法快速完成的操作(例如网络或控制台 I/O)期间持有锁。