1.无状态的对象永远是线程安全的
无状态指的是不是没有属性呢?
2.++count,并不是一个原子操作(单一操作),多线程情况下丢失更改。
@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);
}
}
3.check-then-act,多线程情况下,在check和act之间,可能check的结果已经发生变化
原子操作:操作要么全部都执行,要么都不执行
4.lazy initialization(多线程情况下,这个方法可能返回不同的实例)
@NotThreadSafe
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null)
instance = new ExpensiveObject();
return instance;
}
}
5.Servlet使用AtomicLong统计请求数(java.util.concurrent.atomic)
通过使用线程安全对象来管理Servlet的全部状态,可以维护Servlet的线程安全性。但不一定都是线程安全的
@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);
}
}
6.重进入(这些东西还不是太理解!!!)
当一个线程请求其他线程已经占有的锁时,请求线程将被阻塞。然而内部锁是可重进入的,因此线程在视图获得它自己占有的锁时,请求会成功。重进入异味着锁的请求是基于“没线程(per-thread)”,而不是基于“每调用(pre-invocation)”的。重进入的实现是通过为每个锁关联一个计数和一个占有它的线程。当计数为0时,认为锁是未被占有的。线程请求一个未被占有的琐时,JVM将记录锁的占有者,并且将请求计数置为1。如果同一线程再次请求这个锁,计数将递增;每次占用线程退出同步块,计数器将递减。真到计数器达到0时,锁被释放。
public class Widget {
public synchronized void doSomething() {
...
}
}
public class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
2.4用锁来保护状态(这一节没看懂)
2.5活跃度与性能
如果Service方法声明为Synchronized,因此每次只能有一个线程执行它。这违背了servlet框架的使用初衷——Servlet可以同时处理多个请求——并且当负载过高时会引起用户的不满。如果Servlet正忙于处理一个大数的因式分解,那么在它可以处理一个新的运算开始前,其他用户必须等待,直到当前的请求完成。这种情况下,在多CPU系统中,即使负载很高,仍然会有处理器处于空闲。无论如何,即使运行时间短的请求,仍然可能耗费难以预期长的时间,因为它们必须等待前一个耗时的请求完成。
通过缩小Synchronized块的范围来维护线程安全性,我们很容易提升Servlet的并发性。你应该谨慎地控制Synchronized块不要过小;你不可以将一个原子操作分解到多个Synchronized块中。不过你应该尽量从synchronized块中分离耗时的且不影响共享状态的操作。这样即使在耗时操作的执行过程中,也不会阻止其他线程访问共享状态。
当使用锁的时候,你应该清楚块中的代码的功能,以及它的执行过程是否会很耗时。无论是作运算密集型的操作,还是在执行一个可能存在潜在阻塞的操作,如果线程长时间地占有锁,就会引起活跃度与性能风险的问题。
有些耗时的计算或操作,比如网络或控制台I/O,难以快速完成,执行这些操作期间不要占有锁