有状态与无状态
无状态对象一定是线程安全的
@ThreadSafe
public class StatelessFactorizer extends GenericServlet implements Servlet {
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
encodeIntoResponse(resp, factors);
}
}
原子性
@NotThreadSafe
public class UnsafeCountingFactorizer extends GenericServlet 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);
}
void encodeIntoResponse(ServletResponse res, BigInteger[] factors) {
}
BigInteger extractFromRequest(ServletRequest req) {
return new BigInteger("7");
}
BigInteger[] factor(BigInteger i) {
// Doesn't really factor
return new BigInteger[] { i };
}
}
这是一个有状态的Servlet,而且Count++并非原子操作,导致该Servlet出现线程安全问题。
实际上Count++包含3个独立操作:读取count的值,将值加1,然后将计算结果写入count。
这是一个‘读取-修改-写入’的操作序列,并且结果状态严重依赖于之前的状态。
竞态条件(race condition)
定义:从多进程间通信的角度来讲,是指两个或多个进程对共享的数据进行读或写的操作时,最终的结果取决于这些进程的执行顺序。
最常见的竞态条件就是“先检查后执行”操作,即通过一个可能失效的观测结果来决定下一步的动作。
@NotThreadSafe
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null)
instance = new ExpensiveObject();
return instance;
}
}
class ExpensiveObject { }
线程a和线程b同时执行getInstance(),线程a看到instance为空,创建了一个新的Obj对象,此时线程b也需要判断instance是否为空,此时的instance是否为空取决于不可预测的时序:包括线程a创建Obj对象需要多长时间以及线程的调度方式,如果b检测时,instance为空,那么b也会创建一个instance对象。