稳健的并发程序,须正确使用线程和锁。
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{@Overridepublic void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {// TODO Auto-generated method stubBigInteger 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 factorreturn 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 factorreturn new BigInteger[]{i};}
}