Java编程中实现Condition控制线程通信

java中控制线程通信的方法

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

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

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

4.利用BlockingQueue控制线程通信

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

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

对Lock不了解的可以参考:Java线程同步Lock同步锁代码示例

Condition介绍

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

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 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
释放了
取钱者进入封锁区。。。。。。。。

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

总结

以上就是本文关于Java编程中实现Condition控制线程通信的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

Java线程之锁对象Lock-同步问题更完美的处理方式代码实例

Java线程之线程同步synchronized和volatile详解

创建并运行一个java线程方法介绍

有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!

(0)

相关推荐

  • Java多线程编程中使用Condition类操作锁的方法详解

    Condition的作用是对锁进行更精确的控制.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法.不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的:而Condition是需要与"互斥

  • Java concurrency之Condition条件_动力节点Java学院整理

    Condition介绍 Condition的作用是对锁进行更精确的控制.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法.不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的:而Condition

  • Java中使用Preconditions来检查传入参数介绍

    Preconditions是Guava中的一个类库,用于检查传入参数,一个常见用法如下: boolean findElement(List<String> elements, String desiredElement) { checkNotNull(elements); // ... } 用法简单明了,就是检查参数elements是不是null,如果是null则扔出NullPointerException.当然Preconditions类里还有其它方法,可能满足几乎所有的传入参数的检查.Pr

  • Java编程中实现Condition控制线程通信

    java中控制线程通信的方法 1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyAll()控制线程通信.不灵活. 2.利用Condition控制线程通信,灵活. 3.利用管道pipe进行线程通信,不推荐 4.利用BlockingQueue控制线程通信 本文就讲解利用Condition控制线程通信,非常灵活的方式. Condition类是用来保持Lock对象的协调调用. 对Lock不了解的可以参考:Java线程同步Lock同步锁代码示例

  • Java使用Condition控制线程通信的方法实例详解

    本文实例讲述了Java使用Condition控制线程通信的方法.分享给大家供大家参考,具体如下: 一 点睛 当使用Lock对象来保证同步时,Java提供了一个Condition类来保持协调,使用Condition可以让那些已经得到Lock对象.却无法继续执行的线程释放Lock对象,Condtion对象也可以唤醒其他处于等待的线程. Condition 将同步监视锁方法(wait.notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与Lock对象组合使用,为每个对象提供多

  • Java编程之多线程死锁与线程间通信简单实现代码

    死锁定义 死锁是指两个或者多个线程被永久阻塞的一种局面,产生的前提是要有两个或两个以上的线程,并且来操作两个或者多个以上的共同资源:我的理解是用两个线程来举例,现有线程A和B同时操作两个共同资源a和b,A操作a的时候上锁LockA,继续执行的时候,A还需要LockB进行下面的操作,这个时候b资源在被B线程操作,刚好被上了锁LockB,假如此时线程B刚好释放了LockB则没有问题,但没有释放LockB锁的时候,线程A和B形成了对LockB锁资源的争夺,从而造成阻塞,形成死锁:具体其死锁代码如下:

  • Java使用阻塞队列控制线程通信的方法实例详解

    本文实例讲述了Java使用阻塞队列控制线程通信的方法.分享给大家供大家参考,具体如下: 一 点睛 阻塞队列主要用在生产者/消费者的场景,下面这幅图展示了一个线程生产.一个线程消费的场景: 负责生产的线程不断的制造新对象并插入到阻塞队列中,直到达到这个队列的上限值.队列达到上限值之后生产线程将会被阻塞,直到消费的线程对这个队列进行消费.同理,负责消费的线程不断的从队列中消费对象,直到这个队列为空,当队列为空时,消费线程将会被阻塞,除非队列中有新的对象被插入. BlockingQueue的核心方法:

  • Java编程访问权限的控制代码详解

    本文研究的主要是Java编程访问权限的控制的相关内容,具体介绍如下. 之前没去注意的修饰符,一般变量前面没添加,一个是不知道有什么用,一个是懒,后面遇到项目的时候就会发现私有和公有区别还是很大的. (1)首先是包名 使用一个类的时候,例如集合类,就需要引入这个包,然后再使用该包下面的类.如: package com.myown.iaiti; public class Print { static void print(String s){ System.out.println(s); } } 自

  • java编程中实现调用js方法分析

    本文实例讲述了java编程中实现调用js方法.分享给大家供大家参考,具体如下: /* * 加载脚本引擎,并在java中调用js方法 */ public void test2() { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); try { String str="2&1"

  • 浅谈Java编程中的synthetic关键字

    java synthetic关键字.有synthetic标记的field和method是class内部使用的,正常的源代码里不会出现synthetic field.小颖编译工具用的就是jad.所有反编译工具都不能保证完全正确地反编译class.所以你不能要求太多. 下面我给大家介绍一下synthetic 下面的例子是最常见的synthetic field Java代码 class parent { public void foo() { } class inner { inner() { foo

  • 深入理解Java编程中异常处理的优劣

    Java编程中的异常处理是一个很常见的话题了,几乎任何一门介绍性的Java课程都会提到异常处理.不过,我认为很多人其实没有真正掌握正确处理异常情况的方法和策略,最多也就不过了解个大概,知道概念.我想对三种不同程度和质量的Java异常处理进行了讨论,所阐述的处理异常的方式按手法的高下分为:好,不好和恶劣三种.同时提供了一些解决这些问题的技巧.首先解释一些java异常处理中必须搞清楚的定义和机制.Java语言规范将自Error类或RuntimeException类衍生出来的任何违例都称作"不可检查&

  • Java编程中的构造函数详细介绍

    本文主要是为新手.对java语言感兴趣的人和那些没有系统学习过java基础知识的人进行一个总结,在文章中对构造函数进行了较为详细的说明和讨论,也包含了我个人对于java面向对象中构造函数的一些看法.希望走在java学习道路上的同行者可以有一个较为清晰的认知和理解.当然仅为个人观点,水平有限,不足之处,还请大家多多指出,互相交流学习. 1.构造函数的概念 很多java新手谈到构造函数就会犯晕,我们先来看看什么是构造函数. 首先,构造函数是函数的一种特殊形式,特殊在哪里?构造函数中不需要定义返回类型

  • Java编程中避免equals方法的隐藏陷阱介绍

    摘要 本文描述重载equals方法的技术,这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性. 在<Effective Java>的第8项中,Josh Bloch描述了当继承类作为面向对象语言中的等价关系的基础问题,要保证派生类的equal正确性语义所会面对的困难.Bloch这样写到: 除非你忘记了面向对象抽象的好处,否则在当你继承一个新类或在类中增加了一个值组件时你无法同时保证equal的语义依然正确 在<Programming in Scala>中的第28章演示

随机推荐