稳健的并发程序,须正确使用线程和锁。
java中主要同步机制是关键字synchronized,是一种独占的加锁。
“同步”还包括:volatile类型的变量,显示锁(Explicit Lock),原子变量。
线程安全性:当多个线程访问某个类时,该类始终表现正确的行为,称这个类是线程安全的。
例:一个无状态的Servlet,Servlet框架会创建线程!
import java.io.IOException;
import java.math.BigInteger;
import javax.servlet.GenericServlet;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import net.jcip.annotations.ThreadSafe;
@ThreadSafe
public class StatelessFactorizer extends GenericServlet implements Servlet{
@Override
public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
encodeIntoResponse(resp, factors);
}
void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) {
}
BigInteger extractFromRequest(ServletRequest req) {
return new BigInteger("7");
}
BigInteger[] factor(BigInteger i) {
// Doesn't really factor
return new BigInteger[] { i };
}
}
保证线程安全性
方法1:原子性
参见 http://jcip.net/listings/CountingFactorizer.java
某类中的属性写成
import java.util.concurrent.atomic.AtomicLong;
private final AtomicLong count = new AtomicLong(0);
。。。
方法2:内置锁
java提供内置的锁机制来支持原子性:同步代码块(Synchronized Block),关键字synchronized,线程调用同步代码块就会获得锁。
注意:如果太多的方法是synchronized的,会导致活跃性问题或性能问题。
怎么办?
可能并不是整个方法都是synchronized的,而是方法里某部分代码是synchronized的。
通过缩小同步代码块的范围,既确保Servlet的并发性,又维护线程安全性。同时同步代码块也不能太小,不能将本应是原子的操作拆分开来,应将不是共享且执行时间较长的操作从同步代码块中分离出去。
意思就是多个线程需要“共享的东西”比如某变量放在同步代码块,不是共享的东西放在非同步代码块。
执行时间较长的计算或无法快速完成的操作(例如I/O),一定不要持有锁。
例:http://jcip.net/listings/CachedFactorizer.java
@ThreadSafe
public class CachedFactorizer extends GenericServlet 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);
}
void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) {
}
BigInteger extractFromRequest(ServletRequest req) {
return new BigInteger("7");
}
BigInteger[] factor(BigInteger i) {
// Doesn't really factor
return new BigInteger[]{i};
}
}