在看Condition之前,我们先来看下面这个例子:

工厂类,用来存放、取出商品:

public class Depot {
private int depotSize; //仓库大小
private Lock lock; //独占锁 public Depot(){
depotSize = 0;
lock = new ReentrantLock();
} /**
* 商品入库
* @param value
*/
public void put(int value){
try {
lock.lock();
depotSize += value;
System.out.println(Thread.currentThread().getName() + " put " + value +" ----> the depotSize: " + depotSize);
} finally{
lock.unlock();
}
} /**
* 商品出库
* @param value
*/
public void get(int value){
try {
lock.lock();
depotSize -= value;
System.out.println(Thread.currentThread().getName() + " get " + value +" ----> the depotSize: " + depotSize);
} finally{
lock.unlock();
}
}
}

生产者,生产商品,往仓库里面添加商品:

public class Producer {
private Depot depot; public Producer(Depot depot){
this.depot = depot;
} public void produce(final int value){
new Thread(){
public void run(){
depot.put(value);
}
}.start();
}
}

消费者,消费商品,从仓库里面取出商品:

public class Customer {
private Depot depot; public Customer(Depot depot){
this.depot = depot;
} public void consume(final int value){
new Thread(){
public void run(){
depot.get(value);
}
}.start();
}
}

测试类:

public class Test {
public static void main(String[] args) {
Depot depot = new Depot(); Producer producer = new Producer(depot);
Customer customer = new Customer(depot); producer.produce(10);
customer.consume(5);
producer.produce(20);
producer.produce(5);
customer.consume(35);
}
}

运行结果:

Thread-0 put 10 ----> the depotSize: 10
Thread-1 get 5 ----> the depotSize: 5
Thread-2 put 20 ----> the depotSize: 25
Thread-3 put 5 ----> the depotSize: 30
Thread-4 get 35 ----> the depotSize: -5

程序的运行结果是没有错误的,先put10、然后get5、put20、put5、get35。程序运行结果非常正确,但是在现实生活中,这个实例存在两处错误:

第一:仓库的容量是有限的,我们不可能无限制的往仓库里面添加商品。

第二:仓库的容量是不可能为负数的,但是最后的结果为-5,与现实存在冲突。

针对于上面两处错误,怎么解决?这就轮到Condition大显神通了。

Condition

通过前面几篇博客我们知道Lock提供了比synchronized更加强大、灵活的锁机制,它从某种程度上来说替代了synchronized方式的使用。Condition从字面上面理解就是条件。对于线程而言它为线程提供了一个含义,以便在某种状态(条件Condition)可能为true的另一个线程通知它之前,一直挂起该线程。

对于Condition,JDK API中是这样解释的:

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

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

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其newCondition() 方法。下面我们通过Condition来解决上面的问题:这里只改仓库Depot的代码:

public class Depot {
private int depotSize; //仓库大小
private Lock lock; //独占锁
private int capaity; //仓库容量 private Condition fullCondition;
private Condition emptyCondition; public Depot(){
this.depotSize = 0;
this.lock = new ReentrantLock();
this.capaity = 15;
this.fullCondition = lock.newCondition();
this.emptyCondition = lock.newCondition();
} /**
* 商品入库
* @param value
*/
public void put(int value){
lock.lock();
try {
int left = value;
while(left > 0){
//库存已满时,“生产者”等待“消费者”消费
while(depotSize >= capaity){
fullCondition.await();
}
//获取实际入库数量:预计库存(仓库现有库存 + 生产数量) > 仓库容量 ? 仓库容量 - 仓库现有库存 : 生产数量
// depotSize left capaity capaity - depotSize left
int inc = depotSize + left > capaity ? capaity - depotSize : left;
depotSize += inc;
left -= inc;
System.out.println(Thread.currentThread().getName() + "----要入库数量: " + value +";;实际入库数量:" + inc + ";;仓库货物数量:" + depotSize + ";;没有入库数量:" + left); //通知消费者可以消费了
emptyCondition.signal();
}
} catch (InterruptedException e) {
} finally{
lock.unlock();
}
} /**
* 商品出库
* @param value
*/
public void get(int value){
lock.lock();
try {
int left = value;
while(left > 0){
//仓库已空,“消费者”等待“生产者”生产货物
while(depotSize <= 0){
emptyCondition.await();
}
//实际消费 仓库库存数量 < 要消费的数量 ? 仓库库存数量 : 要消费的数量
int dec = depotSize < left ? depotSize : left;
depotSize -= dec;
left -= dec;
System.out.println(Thread.currentThread().getName() + "----要消费的数量:" + value +";;实际消费的数量: " + dec + ";;仓库现存数量:" + depotSize + ";;有多少件商品没有消费:" + left); //通知生产者可以生产了
fullCondition.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}

test:

public class Test {
public static void main(String[] args) {
Depot depot = new Depot(); Producer producer = new Producer(depot);
Customer customer = new Customer(depot); producer.produce(10);
customer.consume(5);
producer.produce(15);
customer.consume(10);
customer.consume(15);
producer.produce(10);
}
}

运行结果:

Thread-0----要入库数量: 10;;实际入库数量:10;;仓库货物数量:10;;没有入库数量:0
Thread-1----要消费的数量:5;;实际消费的数量: 5;;仓库现存数量:5;;有多少件商品没有消费:0
Thread-4----要消费的数量:15;;实际消费的数量: 5;;仓库现存数量:0;;有多少件商品没有消费:10
Thread-2----要入库数量: 15;;实际入库数量:15;;仓库货物数量:15;;没有入库数量:0
Thread-4----要消费的数量:15;;实际消费的数量: 10;;仓库现存数量:5;;有多少件商品没有消费:0
Thread-5----要入库数量: 10;;实际入库数量:10;;仓库货物数量:15;;没有入库数量:0
Thread-3----要消费的数量:10;;实际消费的数量: 10;;仓库现存数量:5;;有多少件商品没有消费:0

在Condition中,用await()替换wait(),用signal()替换 notify(),用signalAll()替换notifyAll(),对于我们以前使用传统的Object方法,Condition都能够给予实现。

【Java并发编程实战】-----“J.U.C”:Condition的更多相关文章

  1. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

  2. 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock

    ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

  3. 【Java并发编程实战】-----“J.U.C”:ReentrantLock之一简介

    注:由于要介绍ReentrantLock的东西太多了,免得各位客官看累,所以分三篇博客来阐述.本篇博客介绍ReentrantLock基本内容,后两篇博客从源码级别分别阐述ReentrantLock的l ...

  4. 【Java并发编程实战】-----“J.U.C”:CountDownlatch

    上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...

  5. 【Java并发编程实战】-----“J.U.C”:Semaphore

    信号量Semaphore是一个控制访问多个共享资源的计数器,它本质上是一个"共享锁". Java并发提供了两种加锁模式:共享锁和独占锁.前面LZ介绍的ReentrantLock就是 ...

  6. 【Java并发编程实战】-----“J.U.C”:ReentrantLock之三unlock方法分析

    前篇博客LZ已经分析了ReentrantLock的lock()实现过程,我们了解到lock实现机制有公平锁和非公平锁,两者的主要区别在于公平锁要按照CLH队列等待获取锁,而非公平锁无视CLH队列直接获 ...

  7. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  8. 【Java并发编程实战】—– AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形. 其主要从双方面进行了改造:节点的结构与节点等待机制.在结构上引入了 ...

  9. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  10. Java并发编程实战 02Java如何解决可见性和有序性问题

    摘要 在上一篇文章当中,讲到了CPU缓存导致可见性.线程切换导致了原子性.编译优化导致了有序性问题.那么这篇文章就先解决其中的可见性和有序性问题,引出了今天的主角:Java内存模型(面试并发的时候会经 ...

随机推荐

  1. js new

    如果一个函数前面带上new来调用该函数,那么将创建一个隐藏连接到该函数的prototype成员的新对象,同时this将被绑定到那个新对象上 即: function B(){} var a=new B( ...

  2. ubuntu 14.04下spark简易安装

    java安装 首先需要上oracle的网站下载 在lib目录下建立一个jvm文件夹 sudo mkdir /usr/lib/jvm 然后解压文件到这个文件夹 sudo tar zxvf jdk-8u4 ...

  3. Java 之 常用类(一)

    1.字符串: a.分类:String.StringBuffer.StringBuilder b.特殊:①String是唯一一个可以直接用常量赋值的引用数据类型 ②String的常量也是一个对象 (即 ...

  4. 阿里开源消息中间件RocketMQ的前世今生-转自阿里中间件

    昨天,我们将分布式消息中间件RocketMQ捐赠给了开源软件基金会Apache. 孵化成功后,RocketMQ或将成为国内首个互联网中间件在Apache上的顶级项目. 消息一出,本以为群众的反应是这样 ...

  5. Drools 函数学习

    Drools 函数学习 函数是定义在规则文件当中一代码块,作用是将在规则文件当中若干个规则都会用到的业务操作封装起来,实现业务代码的复用,减少规则编写的工作量.函数的编写位置可以是规则文件当中 pac ...

  6. Android进阶系列之源码分析Activity的启动流程

    美女镇楼,辟邪! 源码,是一个程序猿前进路上一个大的而又不得不去翻越障碍,我讨厌源码,看着一大堆.5000多行,要看完得啥时候去了啊.不过做安卓的总有这一天,自从踏上这条不归路,我就认命了.好吧,我慢 ...

  7. IPv6进阶

    IPV6报文部分字段介绍 1.没有校验和字段:优点:当TTL减少时,不需要重新处理,相对于IPV4能减少处理的时间:缺点:必须在上层包含校验和2.下一个报文:可指向扩展报文:(大部分节点不处理和查看大 ...

  8. 无法加载 DLL“SQLite.Interop.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)

    SQLite部署-无法加载 DLL“SQLite.Interop.dll”: 找不到指定的模块 近期刚使用SQLite,主要引用的是System.Data.SQLite.dll这个dll,在部署到测试 ...

  9. iOS判断是模拟器还是真机

    #if TARGET_IPHONE_SIMULATOR //模拟器   #elif TARGET_OS_IPHONE //真机   #endif

  10. flex弹性盒模型布局

    容器属性:1.flex-direction:项目的排列方向(1)row 主轴方向排列(2)row-reverse 主轴反方向排列(3)column 纵向排列(4)column-reverse 纵向反方 ...