return new BigInteger("7");
}
BigInteger[] factor(BigInteger i) {
// Doesn't really factor
return new BigInteger[] { i };
}
}
count是这个对象的状态,虽然递增操作++count是一种紧凑的语法,使其看上去只是一个操作,但是这个操作并非原子的,实际上,它包含了三个独立的操作:读取count的值,将值加1,然后将计算结果写入count。这是一个“读取-修改-写入”的操作序列,并且其结果状态依赖于之前的状态。这样会导致不恰当的执行时序而出现不正确的结果,这种情况称为竞态条件
解决方法:
1、使用AtomicLong类型的变量来统计已处理请求的数量
[java]
import java.math.BigInteger;
import java.util.concurrent.atomic.*;
import javax.servlet.*;
import net.jcip.annotations.*;
@ThreadSafe
public class CountingFactorizer extends GenericServlet 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);
}
}
import java.math.BigInteger;
import java.util.concurrent.atomic.*;
import javax.servlet.*;
import net.jcip.annotations.*;
@ThreadSafe
public class CountingFactorizer extends GenericServlet 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);
}
}
在java.tuil.concurrent.atomic包中包含了一些原子变量类,用于实现在数值和对象引用上的原子状态转换。通过用AtomicLong来代替long类型的计数器,能够确保所有对计数器状态的访问操作都是原子的。
在实际情况中,应尽可能地使用现有的线程安全对象(例如AcomicLong)来管理类的状态)
2、使用加锁:内置锁Synchronized和显式锁Lock
内置锁:
每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视锁。线程在进入同步代码块之前会自动获得锁,并且在退出同步代码块时自动释放锁,而无论是通过正常的控制路劲退出,还是通过从代码块中抛出异常退出。获得内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法
重入性:当某个线程请求一个由其他线程持有的锁时,发出请求的线程就会阻塞。然而,由于内置锁是可重入的,因此如果某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功。
下面程序如果内置锁不是可重入的,那么这段代码将发生死锁
[java]
public class Widget{
public synchronized void doSomething(){
}
}
public class LoggingWidget extends Widget{
public synchronized void doSomething(){
System.out.println(toString() + " : calling doSomething");
super.doSomething();
}
}
public class Widget{
public synchronized void doSomething(){
}
}
public class LoggingWidget extends Widget{
public synchronized void doSomething(){
System.out.println(toString() + " : calling doSomething");
super.doSomething();
}
}在上面代码中,子类改写了父类的synchronized方法,然后调用父类中的方法,此时如果没有可重入的锁,那么这段代码将产生死锁。由于Widget和LoggingWidget中的doSomething方法都是synchronized方法,因此每个doSomething方法在执行前都会获取Widget上的锁。然而,如果内置锁不是可重入的,那么在调用super.doSomething时将无法获得Widget上的锁,因为这个锁已经被持有,从而线程将永远停顿下去,等待一个永远无法获得的锁。
对象的内置锁与与其状态之间没有内在的关联。虽然大多数类都将内置锁用做一种有效的加锁机制,但对象的域并不一定要通过内置锁来保护。当获取与对象关联的锁时,并不能阻止其他线程访问对象,某个线程在获得对象的锁之后,只能阻止其他线程获得同一个锁。之所以每个对象都有一个内置锁,只是为了免去显式地创建锁对象。
对于每个包含多个变量的不变性条件,其中涉及的所有变量都需要由同一个锁来保护
虽然Vector等都具有同步操作,但是并不足以确保Vector上复合操作都是原子的,例如
[java]
if(!vector.contains(e)){
vector.add(e);
}
if(!vector.contains(e)){
vector.add(e);
}
虽然contains和add等方法都是原子方法,但在上面这个“如果不存在则添加”的操作中仍然村在竞态条件。虽然synchronized方法可以确保单个操作的原子性,但如果要把多个操作合并为一个复合操作,还是需要额外的