java中控制线程通信的方法有:1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyAll()控制线程通信。不灵活。

2.利用Condition控制线程通信,灵活。

3.利用管道pipe进行线程通信,不推荐

4.利用BlockingQueue控制线程通信

本文就讲解利用Condition控制线程通信,非常灵活的方式。

Condition类是用来保持Lock对象的协调调用。

对Lock不了解的可以看我的上一篇博客:http://www.cnblogs.com/jycboy/p/5623113.html

Condition介绍

使用Condition可以让那些已经得到lock对象却无法继续执行的线程释放lock对象,Condition对象也可以唤醒处于等待的线程。

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

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

Condition类提供了如下三个方法:

await():造成当前线程在接到信号或被中断之前一直处于等待状态。 该方法流程:1.新建Condition Node包装线程,加入Condition队列。

2.释放当前线程占有的锁

3.阻塞当前线程

signal():唤醒当前lock对象的一个等待线程。signal方法只是将Node(await方法封装的)修改了状态,并没有唤醒线程。要将修改状态后的Node唤醒,一种是再次调用await(),一种是调用unlock()。//这局句很重要,不明白的可以看我下一篇博客。

signalAll():唤醒当前lock对象的所有等待线程。只有当前线程放弃对lock的锁定,被唤醒的线程才可以执行。

代码实例:

代码逻辑:Account类实现同步的取钱(draw)、存钱(deposit)操作;DrawThread循环取钱的线程、DepositThread循环存钱的线程。

Account:

package condition;

import java.util.concurrent.locks.*;
/**
*存钱、取钱
*/
public class Account
{
//显示定义Lock对象
private final Lock lock = new ReentrantLock();//可重入锁
//获得指定Lock对象对应的条件变量
private final Condition cond = lock.newCondition(); //获得condition实例 private String accountNo;
private double balance; //标识账户中是否已经存款的旗标
private boolean flag = false; public Account(){} public Account(String accountNo , double balance)
{
this.accountNo = accountNo;
this.balance = balance;
} public void setAccountNo(String accountNo)
{
this.accountNo = accountNo;
}
public String getAccountNo()
{
return this.accountNo;
} public double getBalance()
{
return this.balance;
}
/**
*取款
* @param drawAmount
*/
public void draw(double drawAmount)
{
//加锁
lock.lock();
System.out.println(Thread.currentThread().getName() +"进入封锁区。。。。。。。。");
try
{
//如果账户中还没有存入存款,该线程等待
if (!flag)
{
cond.await();
}
else
{
//执行取钱操作
System.out.println(Thread.currentThread().getName() +
" 取钱:" + drawAmount);
balance -= drawAmount;
System.out.println("账户余额为:" + balance);
//将标识是否成功存入存款的旗标设为false
flag = false;
//唤醒该Lock对象对应的其他线程
cond.signalAll();
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
//使用finally块来确保释放锁
finally
{
lock.unlock();
System.out.println("释放了");
}
}
/**
* 存款
* @param depositAmount
*/
public void deposit(double depositAmount)
{
lock.lock();
System.out.println(Thread.currentThread().getName() +"进入封锁区。。。。。。。。");
try
{
//如果账户中已经存入了存款,该线程等待
if(flag)
{
System.out.println(Thread.currentThread().getName() +"等待。。。。。。");
cond.await(); }
else
{
//执行存款操作
System.out.println(Thread.currentThread().getName() +
" 存款:" + depositAmount);
balance += depositAmount;
System.out.println("账户余额为:" + balance);
//将标识是否成功存入存款的旗标设为true
flag = true;
//唤醒该Lock对象对应的其他线程
cond.signalAll();
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
//使用finally块来确保释放锁
finally
{
lock.unlock();
System.out.println(Thread.currentThread().getName() +"释放锁。。。。");
}
} public int hashCode()
{
return accountNo.hashCode();
}
public boolean equals(Object obj)
{
if (obj != null && obj.getClass() == Account.class)
{
Account target = (Account)obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
}

  DrawThread:

package condition;

/**
*取钱
*/ public class DrawThread extends Thread
{
//模拟用户账户
private Account account;
//当前取钱线程所希望取的钱数
private double drawAmount; public DrawThread(String name , Account account ,
double drawAmount)
{
super(name);
this.account = account;
this.drawAmount = drawAmount;
} //当多条线程修改同一个共享数据时,将涉及到数据安全问题。
public void run()
{
for (int i = 0 ; i < 6 ; i++ )
{
account.draw(drawAmount);
}
}
}

  DepositThread:

package condition;

/**
*存钱
*/ public class DepositThread extends Thread
{
//模拟用户账户
private Account account;
//当前取钱线程所希望取的钱数
private double depositAmount; public DepositThread(String name , Account account ,
double depositAmount)
{
super(name);
this.account = account;
this.depositAmount = depositAmount;
} //当多条线程修改同一个共享数据时,将涉及到数据安全问题。
public void run()
{
for (int i = 0 ; i < 2 ; i++ )
{
account.deposit(depositAmount);
System.out.println(Thread.currentThread().getName()+" 存钱结束!");
}
}
}

  TestDraw:

package condition;

public class TestDraw
{
public static void main(String[] args)
{
//创建一个账户
Account acct = new Account("1234567" , 0);
new DrawThread("取钱者" , acct , 800).start();
new DepositThread("存钱者甲" , acct , 800).start();
new DepositThread("存钱者乙" , acct , 800).start();
new DepositThread("存钱者丙" , acct , 800).start();
}
}

  运行结果:

取钱者进入封锁区。。。。。。。。
存钱者甲进入封锁区。。。。。。。。
存钱者甲 存款:800.0
账户余额为:800.0
存钱者甲释放锁。。。。
存钱者丙进入封锁区。。。。。。。。
存钱者甲 存钱结束!
存钱者丙等待。。。。。。
存钱者乙进入封锁区。。。。。。。。
存钱者乙等待。。。。。。
释放了
存钱者甲进入封锁区。。。。。。。。
存钱者甲等待。。。。。。
取钱者进入封锁区。。。。。。。。
取钱者 取钱:800.0
账户余额为:0.0
释放了
取钱者进入封锁区。。。。。。。。

这里结果只粘贴了一部分。。。。聪明的你会发现这个程序最后阻塞啦,注意是阻塞不是死锁!阻塞的原因是:三个存钱的线程都运行结束了,但是取钱的线程还没有,所以阻塞啦。

对于await(),signal()方法流程不了解的可以看这篇博客:http://www.cnblogs.com/jycboy/p/5623238.html

转发请注明出处:http://www.cnblogs.com/jycboy/p/5623210.html

Java---Condition控制线程通信的更多相关文章

  1. Condition控制线程通信

    Condition控制线程通信 一.前言 java.util.concurrent.locks.Condition 接口描述了可能会与锁有关联的条件变量.这些变量在用法上与使用Object.wait ...

  2. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...

  3. java多线程 -- Condition 控制线程通信

    Api文档如此定义: Condition 将 Object 监视器方法(wait.notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对 ...

  4. 10. Condition 控制线程通信

    1. 是什么 ? 2. 示例 package com.gf.demo09; import java.util.concurrent.locks.Condition; import java.util. ...

  5. Java并发包——线程通信

    Java并发包——线程通信 摘要:本文主要学习了Java并发包里有关线程通信的一些知识. 部分内容来自以下博客: https://www.cnblogs.com/skywang12345/p/3496 ...

  6. Java并发基础--线程通信

    java中实现线程通信的四种方式 1.synchronized同步 多个线程之间可以借助synchronized关键字来进行间接通信,本质上是通过共享对象进行通信.如下: public class S ...

  7. Java -- 使用阻塞队列(BlockingQueue)控制线程通信

    BlockingQueeu接口是Queue的子接口,但是它的主要作用并不是作为容器,而是作为线程同步的工具. 特征: 当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程 ...

  8. java笔记--关于线程通信

    关于线程通信 使用多线程编程的一个重要原因就是线程间通信的代价比较小 --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3897773.h ...

  9. Java基础教程——线程通信

    线程通信:等待.唤醒 Object方法 这些方法在拥有资源时才能调用 notify 唤醒某个线程.唤醒后不是立马执行,而是等CPU分配 wait 等待,释放锁,不占用CPU资源 notifyAll 唤 ...

随机推荐

  1. Elasticsearch入门介绍

    ES是一个高扩展的.开源的.全文检索的搜索引擎,它提供了近实时的索引.搜索.分析功能. ES文档翻译与总结参考:ES知识汇总 应用场景 1 它提供了强大的搜索功能,可以实现类似百度.谷歌等搜索. 2 ...

  2. SQL Server 2014里的缓存池扩展

    在今天的文章里我想谈下SQL Server 2014里引入的缓存池扩展(Buffer Pool Extensions).我们都知道,在SQL Server里,缓存池是主要的内存消耗者.当你从你存储里读 ...

  3. PHP的反射机制

    在面向对象中最经典的使用就是反射,之前在Java语言中,使用反射可以解耦,用于依赖注入. 在PHP中,同样也有如此强大的地方,我们利用反射来获取一个对象的实例. 首先我们先写一个类: class Te ...

  4. js带上框架和防止被iframe的代码

    1.永远都会带着框架<script language="JavaScript"><!--if (window == top)top.location.href = ...

  5. SRC单一职责原则

    一.定义 一个类应该只有一个发生变化的原因. 二.为什么要使用SRC 因为每一个职责都是变化的一个轴线.当需求变化时,这种变化就会反映为类的职责的变化.如果一个类承担了多于一个的职责,那么引起它变化的 ...

  6. FAILED BINDER TRANSACTION

    最近开发Widget,要在Widget上显示图片,出现了FAILED BINDER TRANSACTION错误,后来才发现是因为图片太大了.Widget使用的是RemoteViews,而Intent传 ...

  7. 我整理的PHP 7.0主要新特性

    原文:http://php.net/manual/en/migration70.new-features.php 1.标量参数类型声明 现在支持字符串(string).整型(int).浮点数(floa ...

  8. Scalaz(0) - 写在前面

    面向对象编程范畴(OOP)从80年代C++到90年代java的兴起已经经历了几十年的高潮,是不是已经发展到了尽头,该是函数式编程(FP)开始兴旺发达的时候了吧.这样说似乎心眼儿有点坏,可能会得罪当今大 ...

  9. 性能测试学习之二 ——性能测试模型(PV计算模型)

    PV计算模型 现有的PV计算公式是: 每台服务器每秒平均PV量 =( (总PV*80%)/(24*60*60*40%))/服务器数量 =2*(总PV)/* (24*60*60) /服务器数量 通过定积 ...

  10. jQuery validate 根据 asp.net MVC的验证提取简单快捷的验证方式(jquery.validate.unobtrusive.js)

    最近在学习asp.netMVC,发现其中的验证方式书写方便快捷,应用简单,易学好懂. 验证方式基于jQuery的validate 验证方式,也可以说是对jQuery validate的验证方式的扩展, ...