锁对象

  • 有两种机制防止代码块受并发访问的干扰。Java语言提供一个synchronized关键字达到这一目的,并且Java SE 5.0引入了ReentrantLook类。synchronized关键字自动提供一个锁以及相关的"条件",对于大多数需要显式锁的情况,这是很便利的。java.util.concurrent框架为这些基础机制提供了独立的类。
  • 如果使用锁来保护模拟银行转账中的Bank类的transfer方法
public class Bank {
    private Lock bankLock = new ReentrantLock();
    //把钱从一个帐户转到另一个帐户
    public void transfer(int from, int to, double amount)
    {
        bankLock.lock();
        try{
            if (accounts[from] < amount) return;
            System.out.print(Thread.currentThread());
            accounts[from] -= amount;
            System.out.printf(" %10.2f from %d to %d", amount, from, to);
            accounts[to] += amount;
            System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
        }finally {
            bankLock.unlock();
        }
    }
}
  • 假定一个线程调用transfer,在执行结束前被剥夺了运行权。假定第二个线程也调用了transfer,由于第二个线程不能获得锁,将在调用lock方法时被阻塞。它必须等待第一个线程完成transfer方法的执行之后才能再度被**。当第一个线程释放锁时,那么第二个线程才开始运行。
    锁对象
  • 注意每一个Bank对象有自己的ReentrantLock对象。如果两个线程试图访问同一个Bank对象,那么锁以串行方式提供服务。但是,如果两个线程访问不同的Bank对象,每个线程得到不同的锁对象,两个线程都不会发生阻塞。本该如此,因为线程在操纵不同的Bank实例的时候,线程之间不会相互影响。模拟银行转账就是使用同一个Bank对象,才能控制余额不会出现讹误。
  • 锁是可以重入的,因为线程可以重复地获得已经持有的锁。锁保持一个持有计数来跟踪对lock方法的嵌套调用。线程在每一次调用lock都要调用unlock来释放锁。由于这一特性,被一个锁保护的代码可以调用另一个使用相同的锁的方法。例如,transfer方法调用getTotalBalance方法,这也会*bankLock对象,此时bankLock对象的持有计数为2。当getTotalBalance方法退出的时候,持有计数变回1.当transfer方法退出的时候,持有计数变为0。线程释放锁。