多线程学习(七)——线程通信之使用Condition通信

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

猜你喜欢

转载自blog.csdn.net/ruijiao_ren/article/details/79397744