线程协作-Condition介绍

    上一篇文章里讲了java.util.concurrent.locks 包下的Lock,以及简单使用的例子,这一篇我们要继续介绍java.util.concurrent.locks包下的类文件,它就是Condition

 

一:源码解读

package java.util.concurrent.locks;

import java.util.concurrent.*;
import java.util.Date;

/**
 * Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,
 * 以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)
 * 其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用
 */
public interface Condition {

	/**
	 * 造成当前线程在接到信号或被中断之前一直处于等待状态。
	 */
	void await() throws InterruptedException;
	
	/**
     * 造成当前线程在接到信号之前一直处于等待状态。
     */
	void awaitUninterruptibly();

    /**
     * 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
     */
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    /**
     * 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
     */
    boolean await(long time, TimeUnit unit) throws InterruptedException;

    /**
     * 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
     */
    boolean awaitUntil(Date deadline) throws InterruptedException;

    /**
     * 唤醒一个等待线程。    
     */
    void signal();

    /**
     * 唤醒所有等待线程。
     */
    void signalAll();
}

 

说明通过查看Condition的源码,大致了解到两个要点

  •  它是一个接口
  •   它提供的一系列方法都是用来阻塞或唤醒线程用的。(功能类似JavaObject类的wait()notify()notifyAll()方法)

JDK的官方解释如下:

 

条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。 

 

 

JDK的源码中,还提供了一个关于Condition示例,示例如下:

 

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BoundedBuffer {
	//创建锁
	final Lock lock = new ReentrantLock();
	
	final Condition notFull = lock.newCondition();
	final Condition notEmpty = lock.newCondition();
	
	final Object[] items = new Object[100];
	
	int putptr, takeptr, count;
	
	public void put(Object x) throws InterruptedException{
		lock.lock();//加锁
		try{
			while(count == items.length){
				notFull.await(); //造成当前线程在接到信号或被中断之前一直处于等待状态。
			}
			items[putptr] = x;
			if (++putptr == items.length) putptr = 0;
			++count;
			notEmpty.signal(); //唤醒一个等待线程。
		}finally{
			lock.unlock();//解锁
		}
	}
	
	public Object take() throws InterruptedException{
		lock.lock();//加锁
		try{
			while(count == 0){
				notEmpty.await();
			}
			Object x = items[takeptr];
			if (++takeptr == items.length) takeptr = 0;
			--count;
			notFull.signal();
			return x;
		}finally{
			lock.unlock();//解锁
		}
	}
}

 

这里使用了ReentrantLock锁和Condition对象给出了有界缓存的实现,即使用Condition,分别为notFull notEmpty。当缓存为空时,take将阻塞并等待notEmpty,此时putnotEmpty发送信号,可以解除任何在take中阻塞的线程。

 

 

二:实际运用

 

我们要打印199个数字,由A线程先打印123,然后由B线程打印456,然后再由A线程打印789. 这道题有很多种解法,现在我们使用Condition来做这道题。

 

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PrintDemo {

	//申请锁
	final Lock lock = new ReentrantLock();
	final Condition conditionA = lock.newCondition();
	final Condition conditionB = lock.newCondition();
	
	private String worker = "A"; //默认是A先工作
	private int endNum = 9;
	private int i = 1;
	
	public static void main(String[] args){
		PrintDemo pd = new PrintDemo();
		pd.print();
	}
	
	public void print(){
		new Thread(new Runnable() {
			@Override
			public void run() {
				lock.lock();//加锁
				try{
					while(i<endNum){
						while(!worker.equals("A")){
						   conditionA.await(); //如果worker不是A,表示A不需要工作,则处于休眠等待
						}
						System.out.println(i + "--" + Thread.currentThread().getName());
						if(i%3==0){
							worker = "B";
							conditionB.signal();
						}
						i++;
					}
					System.out.println(i + "--" + Thread.currentThread().getName());
				}catch(InterruptedException e){
					e.printStackTrace();
				}finally{
					lock.unlock();//解锁
				}
			}
		}, "threadA").start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				lock.lock();//加锁
				try{
					while(i<endNum){
						while(!worker.equals("B")){
						   conditionB.await(); //如果worker不是B,表示B不需要工作,则处于休眠等待
						}
						System.out.println(i + "--" + Thread.currentThread().getName());
						if(i%3==0){
							worker = "A";
							conditionA.signal();
						}
						i++;
					}
				}catch(InterruptedException e){
					e.printStackTrace();
				}finally{
					lock.unlock();//解锁
				}
			}
		}, "threadB").start();
	}
}

 

 

 

运行结果:

  1--threadA

  2--threadA

  3--threadA

  4--threadB

  5--threadB

  6--threadB

  7--threadA

  8--threadA

  9--threadA

 

  通过使用Condition,有效了使线程A和线程B进行协作工作,A打印完1、2、3后,B打印4、5、6,然后A再打印7、8、9.其实这里只是简单的使用例子,真正的使用环境并不是打印字母这么简单。

 

  在这里,我想抛出一个问题:为什么一个Lock可以创建两个Condition,它是怎么实现的?

 

 

参考资料:

   http://blog.csdn.net/ghsau/article/details/7481142

   http://ifeve.com/understand-condition/

   http://www.th7.cn/Program/java/201308/147915.shtml

   http://outofmemory.cn/java/java.util.concurrent/lock-reentrantlock-condition

  

  

猜你喜欢

转载自15838341661-139-com.iteye.com/blog/2229385
今日推荐