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版》)