Condition的使用

jdk1.5提供的api,位于java.util.concurrent.locks 包下,是一个接口, 常用方法有:

方法描述
void await()造成当前线程在接到信号或被中断之前一直处于等待状态。
boolean await(long time, TimeUnit unit)造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
long awaitNanos(long nanosTimeout)造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
void awaitUninterruptibly()造成当前线程在接到信号之前一直处于等待状态。
boolean awaitUntil(Date deadline)造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
void signal()唤醒一个等待线程。
void signalAll()唤醒所有等待线程。

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

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

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

官方示例

实现一个缓冲队列,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。

class BoundedBuffer {
   final Lock lock = new ReentrantLock();//定义锁对象
   //通过锁对象获取Condition实例
   final Condition notFull  = lock.newCondition(); //用于控制put操作
   final Condition notEmpty = lock.newCondition(); //用于控制take操作

   final Object[] items = new Object[100];//缓冲队列,初始容量100
   int putptr, takeptr, count;//分别记录put,take当前的索引,count用于记录当前item的个数
	
   /**
   * 往缓冲队列中添加数据
   */
   public void put(Object x) throws InterruptedException {
    //上锁,作用和synchronized一样,保证代码同一时刻只有一个线程可以操作,也保证了和take方法的互斥
     lock.lock();
     try {
       while (count == items.length){ 
         notFull.await();//如果队列满了,则put线程等待被唤醒
       }
       items[putptr] = x; //队列未满,则添加数据
       if (++putptr == items.length) putptr = 0;//添完后,如果记录读数据的索引到了最后一个位置,则重置为0
       ++count;//item总数自增
       notEmpty.signal();//唤醒take线程取数据
     } finally {
       lock.unlock();//put操作完后,释放锁.
     }
   }
	
   /**
   * 从缓冲队列中取数据
   */
   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0){ 
         notEmpty.await();//如果队列空了,则take线程等待被唤醒
       }
       Object x = items[takeptr]; //队列未空,则取数据
       if (++takeptr == items.length) takeptr = 0;//取完后,如果记录取数据的索引到了最后一个位置,则重置为0
       --count;//item总数自减
       notFull.signal();//唤醒put线程添加数据
       return x;//返回取得的数据
     } finally {
       lock.unlock();//take操作完后,释放锁对象
     }
   } 
 }

示例2

这个例子是用来实现如下功能,主线程运行5次,然后通知线程1运行5次,然后线程1通知线程2运行5次,接着又由线程2通知主线程运行5次,如此反复执行3次.

分析这个需求的时候,首先应该想到这里一共有3个线程,且每个线程在运行的时候,其它线程不能打扰,也就是说这3个线程需要共用同一个锁对象,即这3个线程需要访问同一个共享资源,这里可以有一个很好的解决思路就是将这3个线程各自运行5次的逻辑(方法)封装到同一个类中.这样,我们使用Lock或者synchronized的时候,只要锁对象是同一个,就能保证这3个方法的互斥性了.

然后,需要保证的是这3个线程在执行各自方法的时候是按照一定的顺序执行的,这里使用Condition就能很好的解决这个问题,需要用到3个Condition实例,第一个用于主线程与线程1通信,第二个用于线程1和线程2通信,第三个用于线程2和主线程通信.

package com.example;

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

/**
 * Created by mChenys on 2016/5/15.
 */
public class ThreeConditionCommunication {
    /**
     * @param args
     */
    public static void main(String[] args) {
        //定义共享资源
        final Business business = new Business();

        //线程2执行
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 3; i++) {
                    business.doSub2(i);
                }
            }
        }, "Thread-1").start();

        //线程3执行
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 3; i++) {
                    business.doSub3(i);
                }
            }
        }, "Thread-2").start();


        //主线程执行
        for (int i = 1; i <= 3; i++) {
            business.doMain(i);
        }
    }

    /**
     * 共享资源
     */
    static class Business {
        //定义公共锁
        Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();//控制主线程运行
        Condition condition2 = lock.newCondition();//控制线程2执行
        Condition condition3 = lock.newCondition();//控制线程3执行
        private int shouldSub = 1;//标记当前那个线程运行的标记,1为主线程,2为线程2,3为线程3

        public void doMain(int i) {
            lock.lock();//上锁
            try {
                while (shouldSub != 1) {
                    try {
                        condition1.await();//主线程等待
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 5; j++) {
                    System.out.println("doMain thread sequence of " + j + ",loop of " + i);
                }
                shouldSub = 2;
                condition2.signal();//唤醒线程2
            } finally {
                lock.unlock();//释放锁
            }
        }

        public void doSub2(int i) {
            lock.lock();//上锁
            try {
                while (shouldSub != 2) {
                    try {
                        condition2.await();//线程2等待
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 5; j++) {
                    System.out.println("doSub2 thread sequence of " + j + ",loop of " + i);
                }
                shouldSub = 3;
                condition3.signal();//唤醒线程3
            } finally {
                lock.unlock();//释放锁
            }
        }

        public void doSub3(int i) {
            lock.lock();//上锁
            try {
                while (shouldSub != 3) {
                    try {
                        condition3.await();//线程3等待
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 5; j++) {
                    System.out.println("doSub3 thread sequence of " + j + ",loop of " + i);
                }
                shouldSub = 1;
                condition1.signal();//唤醒主线程
            } finally {
                lock.unlock();//释放锁
            }
        }



    }
}

运行结果:

doMain thread sequence of 1,loop of 1
doMain thread sequence of 2,loop of 1
doMain thread sequence of 3,loop of 1
doMain thread sequence of 4,loop of 1
doMain thread sequence of 5,loop of 1
doSub2 thread sequence of 1,loop of 1
doSub2 thread sequence of 2,loop of 1
doSub2 thread sequence of 3,loop of 1
doSub2 thread sequence of 4,loop of 1
doSub2 thread sequence of 5,loop of 1
doSub3 thread sequence of 1,loop of 1
doSub3 thread sequence of 2,loop of 1
doSub3 thread sequence of 3,loop of 1
doSub3 thread sequence of 4,loop of 1
doSub3 thread sequence of 5,loop of 1
doMain thread sequence of 1,loop of 2
doMain thread sequence of 2,loop of 2
doMain thread sequence of 3,loop of 2
doMain thread sequence of 4,loop of 2
doMain thread sequence of 5,loop of 2
doSub2 thread sequence of 1,loop of 2
doSub2 thread sequence of 2,loop of 2
doSub2 thread sequence of 3,loop of 2
doSub2 thread sequence of 4,loop of 2
doSub2 thread sequence of 5,loop of 2
doSub3 thread sequence of 1,loop of 2
doSub3 thread sequence of 2,loop of 2
doSub3 thread sequence of 3,loop of 2
doSub3 thread sequence of 4,loop of 2
doSub3 thread sequence of 5,loop of 2
doMain thread sequence of 1,loop of 3
doMain thread sequence of 2,loop of 3
doMain thread sequence of 3,loop of 3
doMain thread sequence of 4,loop of 3
doMain thread sequence of 5,loop of 3
doSub2 thread sequence of 1,loop of 3
doSub2 thread sequence of 2,loop of 3
doSub2 thread sequence of 3,loop of 3
doSub2 thread sequence of 4,loop of 3
doSub2 thread sequence of 5,loop of 3
doSub3 thread sequence of 1,loop of 3
doSub3 thread sequence of 2,loop of 3
doSub3 thread sequence of 3,loop of 3
doSub3 thread sequence of 4,loop of 3
doSub3 thread sequence of 5,loop of 3

从运行结果,可以看出每个线程在执行的时候都是完整完成的,不会受到其它线程的打扰,同时各线程间的运行都是按照事先定好的规则来实现的,这就体现了Condition的强大之处,它能完成wait、notify所不能完成的事情,wait、notify 只能控制2个线程间的通信,而Condition可以控制n个线程间的通信.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值