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

 更新时间:2017年11月14日 08:44:27   作者:超超boy  
这篇文章主要介绍了Java编程中实现Condition控制线程通信,简单介绍了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线程方法介绍

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

相关文章

  • Lombok不生效,提示java: 找不到符号的解决方案

    Lombok不生效,提示java: 找不到符号的解决方案

    这篇文章主要介绍了Lombok不生效,提示java: 找不到符号的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • Spring事务失效场景的详细整理

    Spring事务失效场景的详细整理

    Spring 事务的传播特性说的是,当多个事务同时存在的时候,Spring 如何处理这些事务的特性,下面这篇文章主要给大家介绍了关于Spring事务失效场景的相关资料,需要的朋友可以参考下
    2022-02-02
  • spring-data-redis连接操作redis的实现

    spring-data-redis连接操作redis的实现

    spring-data-redis则是对Jedis进行了高度封装,使用起来非常方便。本文主要介绍了spring-data-redis连接操作redis的实现,感兴趣的可以了解一下
    2021-07-07
  • 在springboot3微项目中如何用idea批量创建单元测试逻辑

    在springboot3微项目中如何用idea批量创建单元测试逻辑

    这篇文章主要介绍了在SpringBoot3项目中使用IntelliJIDEA批量创建单元测试包括准备工作(确保项目配置正确,添加测试依赖),使用IntelliJIDEA创建测试,感兴趣的朋友一起看看吧
    2024-10-10
  • Java实现Kruskal算法的示例代码

    Java实现Kruskal算法的示例代码

    Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。本文将介绍用Java语言实现Kruskal算法的示例代码,需要的可以参考一下
    2022-07-07
  • 在SpringBoot微服务中设置和管理多个数据库的代码示例

    在SpringBoot微服务中设置和管理多个数据库的代码示例

    在现代微服务架构中,通常需要与多个数据库交互的服务,这可能是由于各种原因,例如遗留系统集成、不同类型的数据存储需求,或者仅仅是为了优化性能,在本综合指南中,我们将探讨如何在 Spring Boot 微服务中设置和管理多个数据库连接,需要的朋友可以参考下
    2024-12-12
  • Spring配置文件中密码明文改为密文处理的通用方式

    Spring配置文件中密码明文改为密文处理的通用方式

    SpringBoot和SpringCloud中涉及多个配置文件,配置文件中对于密码默认是明文方式,这种方式在生产环境一般是不被允许的,为避免配置文件中出现明文,应当在配置文件中配置为密文,然后在启动时在程序内部完成解密,本文提供了通用的处理方式,需要的朋友可以参考下
    2025-01-01
  • 梳理总结Java static关键字的方法作用

    梳理总结Java static关键字的方法作用

    这篇文章主要介绍了梳理总结Java static关键字的方法作用, static 关键字可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的
    2022-06-06
  • 解决java中的父类私有成员变量的继承问题

    解决java中的父类私有成员变量的继承问题

    这篇文章主要介绍了解决java中的父类私有成员变量的继承问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • 基于Restful接口调用方法总结(超详细)

    基于Restful接口调用方法总结(超详细)

    下面小编就为大家带来一篇基于Restful接口调用方法总结(超详细)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08

最新评论