欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-04-01 22:10:52
java中控制线程通信的方法 1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyall()控制线程通信。不灵活...

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详解

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