synchronized是隐式同步监视器,它可以使用wait(),notify(),notifyAll()方法来进行线程通信。
使用Lock对象来保证同步,需要使用java提供的Condition类来保持协调,使用Condition可以让那些已经得到Lock对象却无法继续执行的线程释放Lock对象,Condition对象也可以唤醒其他处于等待的线程。
Condition将同步监视器方法(wait(),notify(),notifyAll())分解为截然不同的对象,这样可以更加方便的将这些对象和Lock对象组合使用,为每个对象提供多个等待集(wait-set)。这样,Lock替代了同步方法或者同步代码快,Condition替代了同步监视器。
Condition实例需要绑定在一个Lock对象上,要获得特定Lock实例的Condition实例,调用Lock对象的newCondition()方法就可以了。Condition类提供了下面三个方法:
await():类似于隐式同步器上的wait()方法,导致当前线程等待,直到其他线程调用该Condition的signal()或者signalAll()方法来唤醒该线程,await()有很多的变体,如下面,可以完成更丰富的等待操作:
long awaitNanos(long nanosTimeout)
void awaitUninterruptibly()
awaitUntil(Date deadline)
signal():唤醒在此Lock对象上等待的单个线程。若有多个线程等待,则会唤醒其中任意一个线程,只有当前线程放弃对Lock对象的锁定后(使用await()方法),才可以执行被唤醒的线程。
signalAll():唤醒在此Lock对象上等待的所有线程。只有当前线程放弃对Lock对象的锁定后,才可以执行被唤醒的线程。
将Account类改为如下代码,其他类和之前博文"多线程学习(六)——线程通信之传统线程通信"一样,运行结果也没有变:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class Account { //显式定义Lock锁 private final ReentrantLock lock = new ReentrantLock(); //获得指定Lock对象对应的Condition private final Condition cond = lock.newCondition(); //设置标志位flag private boolean flag = true; //封装账户编号、账户余额的两个成员变量 private String accountNo; private double balance; public Account() {} //构造器 public Account(String accountNo,double balance) { this.accountNo = accountNo; this.balance = balance; } public String getAccountNo() { return accountNo; } public void setAccountNo(String accountNo) { this.accountNo = accountNo; } //因为账户余额不允许随便修改,所以只为balance提供了getter方法 public double getBalance() { return this.balance; } //提供一个线程安全的draw()方法来完成取钱操作 public void draw(double drawMoney) { //加锁 lock.lock(); try { //表示有钱 if(flag) { //执行取钱操作 System.out.println(Thread.currentThread().getName()+" 取钱:"+drawMoney); balance -= drawMoney; System.out.println("账户余额为:"+balance); //将标志位变为false,表示账户种已经没有钱 flag = false; //唤醒其他线程 cond.signalAll(); }else {//表示没钱 cond.await(); } }catch (InterruptedException e) { e.printStackTrace(); } //使用finally块来释放锁 finally { lock.unlock(); } } //提供一个线程安全的deposit()方法来完成存钱操作 public void deposit(double depositMoney) { //加锁 lock.lock(); try { //有钱 if(flag) { cond.await(); }else { //执行存款操作 System.out.println(Thread.currentThread().getName()+" 存款:" + depositMoney); balance+=depositMoney; System.out.println("账户余额为:"+balance); //账户种有钱,并唤醒其他线程 flag = true; cond.signalAll(); } }catch(InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } //下面两个方法根据accountNo来重写hashCode()和equals()方法 public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if(this == obj) return true; if(obj!=null && obj.getClass() == Account.class) { Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false; } }
从上面代码可以看到显式地使用Lock对象来充当同步监视器,则需要Condition对象来暂停、唤醒制定线程。
(参考《疯狂Java讲义第3版》)