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. Android SDK Manager无法更新的解决

    只需要配置一下hosts文件,在文件的末尾添加下面一句: 74.125.237.1 dl-ssl.google.com windows系统中hosts文件的位置为:C:\Windows\System3 ...

  2. 移动设计必备:iPhone 5S PSD 矢量原型免费下载

    正如预期的那样,苹果公司最近发布了 iPhone 5S——其最新的旗舰级智能手机.新发布的 iPhone 5s,搭载了 iOS 7,看上去和 iPhone 5 几乎一模一样的,有岩石灰.金色以及银色三 ...

  3. VS2015 新Web项目(C#6)出现CS1617错误的解决

    VS2015新增了对C#6的支持. 在新的Web项目模板中通过引入nuget包Microsoft.CodeDom.Providers.DotNetCompilerPlatform:1.0.0并在web ...

  4. 浅谈Dictionary用法

    一.基础篇 1.Dictionary泛型类提供了从一组键到一组值的映射,即键和值的集合类. 2.Dictionary通过键来检索值的速度是非常快的,这是因为 Dictionary 类是作为一个哈希表来 ...

  5. LeetCode - 52. N-Queens II

    52. N-Queens II Problem's Link --------------------------------------------------------------------- ...

  6. 从C#到Objective-C,循序渐进学习苹果开发(4)--代码块(block)和错误异常处理的理解

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...

  7. vs2010 用户控件拖到aspx页面不可用

    错误描述: 在web项目中添加一个用户控件,直接拖动用户控件ascx到aspx页面出现a标签而不是控件标签 解决办法: 把“源”切换为“设计”视图,然后拖动ascx用户控件到页面即可:

  8. jquery_layout

    http://layout.jquery-dev.com/documentation.cfm

  9. Newtonsoft.Json 用法

    Newtonsoft.Json 是.NET 下开源的json格式序列号和反序列化的类库.官方网站: http://json.codeplex.com/ 使用方法 1.首先下载你需要的版本,然后在应用程 ...

  10. MongoDB2.6 新特性

    MongoDB 2.6 已经在今天发布,当然,关注MongoDB的同志自然知道这个消息. 整个MongoDB2.6,个人觉得认证系统是一个重新设计的系统.当然,MongoDB 2.4.6开始,就已经在 ...