不同功能的线程之间有时候是需要相互交换信息的,有几种线程之间通信的方法。


1、传统的线程通信借助于Object类的三个方法,分别是wait()、notify()、notifyAll()。使用这三种方法来实现线程通信的一般是用synchronized实现线程同步的类。

        wait():让当前线程处在暂停状态,也就是阻塞状态,直到阻塞之间结束或者是其他线程调用该同步监视器的notify()方法或者是notifyAll()方法来唤醒该线程。

        notify():唤醒该同步监视器上等待的单个线程,

        notifyAll():唤醒此同步监视器上等待的所有线程

       举个例子:

//简易替代取钱的方法,存钱和取钱交叉进行,flag为一个标记
	public synchronized void draw(double drawAmount){
		try	{
			// 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
			if (!flag){
				wait();
			}
			else{
				// 执行取钱
				System.out.println(Thread.currentThread().getName()+"取钱:"+drawAmount);
				balance -= drawAmount;
				System.out.println("账户余额为:" + balance);
				// 将标识账户是否已有存款的旗标设为false。
				flag = false;
				// 唤醒其他线程
				notifyAll();
			}
		}
		catch (InterruptedException ex){
			ex.printStackTrace();
		}
	}
	public synchronized void deposit(double depositAmount){
		try{
			// 如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
			if (flag){
				wait();
			}
			else{
				// 执行存款
				System.out.println(Thread.currentThread().getName()
					+ " 存款:" +  depositAmount);
				balance += depositAmount;
				System.out.println("账户余额为:" + balance);
				// 将表示账户是否已有存款的旗标设为true
				flag = true;
				// 唤醒其他线程
				notifyAll();
			}
		}
		catch (InterruptedException ex){
			ex.printStackTrace();
		}
	}



2、Condition类控制线程通信

        当程序不使用synchronized实现同步,而是显式的使用Lock对象来保证同步时,系统中不存在隐式的同步监控器,所以不能使用wait()、notifyAll()等方法来进行线程通信。针对这种情况,Java提供了Condition类。这个类可以让那些已经得到Lock对象却无法继续执行的线程释放Lock对象,Condition对象也可以唤醒其他的处于等待的线程。

        这种情况下,Lock替代了同步方法或者是同步代码块,而Condition替代了同步监视器的功能。Condition提供了下面三种方法:await()方法类似于wait()方法。signal()方法类似于notify()方法。signalAll()类似于notifyAll方法。

        当显式的使用Lock对象来充当同步监视器时,需要使用Condition对象来暂停、唤醒指定线程。具体的Condition类的创建方法,用下面的例子来说明,关键之处在于针对Lock类对象指定一个Condition对象

public class Account
{
	// 显式定义Lock对象
	private final Lock lock = new ReentrantLock();
	// 获得指定Lock对象对应的Condition
	private final Condition cond  = lock.newCondition(); 
	
	// 封装账户编号、账户余额两个Field
	private String accountNo;
	private double balance;
	//标识账户中是否已有存款的旗标
	private boolean flag = false;

	// ......省略了很多代码段,包括getter、setter和构造器等等
	
	public void draw(double drawAmount)
	{
		// 加锁
		lock.lock();
		try{
			// 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
			if (!flag){
				cond.wait();
			}
			else{
				// 执行取钱
				System.out.println(Thread.currentThread().getName()+"取钱:"+drawAmount);
				balance -= drawAmount;
				System.out.println("账户余额为:" + balance);
				// 将标识账户是否已有存款的旗标设为false。
				flag = false;
				// 唤醒其他线程
				cond.signalAll();
			}
		}
		catch(InterruptedException ex){
			ex.printStackTrace();
		}
		// 使用finally块来释放锁
		finally{
			lock.unlock();
		}
	}
	public void deposit(double depositAmount){
		lock.lock();
		try{
			// 如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
			if (flag){
				cond.wait();
			}
			else{
				// 执行存款
				System.out.println(Thread.currentThread().getName()+"存款"+depositAmount);
				balance += depositAmount;
				System.out.println("账户余额为:" + balance);
				// 将表示账户是否已有存款的旗标设为true
				flag = true;
				// 唤醒其他线程
				cond.signalAll();
			}
		}
		catch(InterruptedException ex){
			ex.printStackTrace();
		}
		// 使用finally块来释放锁
		finally{
			lock.unlock();
		}
	}



3、使用

BlockingQueue控制线程通信

        Java 5 提供了一个更方便的线程通信工具,BlockingQueue,它虽然是Queue的子接口,但是他的主要用途并不是作为容器,而是作为线程同步工具,这决定于BlockingQueue的特征:当生产者线程视图想BlockingQueue中放入元素时,若该队列已满,则线程被阻塞。当消费者线程试图从BlockingQueue中获取元素时,若队列已空,则该线程被阻塞。