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.新建ConditionNode包装线程,加入Condition队列。
2.释放当前线程占有的锁
3.阻塞当前线程
signal():唤醒当前lock对象的一个等待线程。signal方法只是将Node(await方法封装的)修改了状态,并没有唤醒线程。要将修改状态后的Node唤醒,一种是再次调用await(),一种是调用unlock()。//这局句很重要,不明白的可以看我下一篇博客。
signalAll():唤醒当前lock对象的所有等待线程。只有当前线程放弃对lock的锁定,被唤醒的线程才可以执行。
代码实例:
代码逻辑:Account类实现同步的取钱(draw)、存钱(deposit)操作;DrawThread循环取钱的线程、DepositThread循环存钱的线程。
Account:
packagecondition;
importjava.util.concurrent.locks.*;
/**
*存钱、取钱
*/
publicclassAccount
{
//显示定义Lock对象
privatefinalLocklock=newReentrantLock();//可重入锁
//获得指定Lock对象对应的条件变量
privatefinalConditioncond=lock.newCondition();//获得condition实例
privateStringaccountNo;
privatedoublebalance;
//标识账户中是否已经存款的旗标
privatebooleanflag=false;
publicAccount(){}
publicAccount(StringaccountNo,doublebalance)
{
this.accountNo=accountNo;
this.balance=balance;
}
publicvoidsetAccountNo(StringaccountNo)
{
this.accountNo=accountNo;
}
publicStringgetAccountNo()
{
returnthis.accountNo;
}
publicdoublegetBalance()
{
returnthis.balance;
}
/**
*取款
*@paramdrawAmount
*/
publicvoiddraw(doubledrawAmount)
{
//加锁
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(InterruptedExceptionex)
{
ex.printStackTrace();
}
//使用finally块来确保释放锁
finally
{
lock.unlock();
System.out.println("释放了");
}
}
/**
*存款
*@paramdepositAmount
*/
publicvoiddeposit(doubledepositAmount)
{
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(InterruptedExceptionex)
{
ex.printStackTrace();
}
//使用finally块来确保释放锁
finally
{
lock.unlock();
System.out.println(Thread.currentThread().getName()+"释放锁。。。。");
}
}
publicinthashCode()
{
returnaccountNo.hashCode();
}
publicbooleanequals(Objectobj)
{
if(obj!=null&&obj.getClass()==Account.class)
{
Accounttarget=(Account)obj;
returntarget.getAccountNo().equals(accountNo);
}
returnfalse;
}
}
DrawThread:
packagecondition;
/**
*取钱
*/
publicclassDrawThreadextendsThread
{
//模拟用户账户
privateAccountaccount;
//当前取钱线程所希望取的钱数
privatedoubledrawAmount;
publicDrawThread(Stringname,Accountaccount,
doubledrawAmount)
{
super(name);
this.account=account;
this.drawAmount=drawAmount;
}
//当多条线程修改同一个共享数据时,将涉及到数据安全问题。
publicvoidrun()
{
for(inti=0;i<6;i++)
{
account.draw(drawAmount);
}
}
}
DepositThread:
packagecondition;
/**
*存钱
*/
publicclassDepositThreadextendsThread
{
//模拟用户账户
privateAccountaccount;
//当前取钱线程所希望取的钱数
privatedoubledepositAmount;
publicDepositThread(Stringname,Accountaccount,
doubledepositAmount)
{
super(name);
this.account=account;
this.depositAmount=depositAmount;
}
//当多条线程修改同一个共享数据时,将涉及到数据安全问题。
publicvoidrun()
{
for(inti=0;i<2;i++)
{
account.deposit(depositAmount);
System.out.println(Thread.currentThread().getName()+"存钱结束!");
}
}
}
TestDraw:
packagecondition;
publicclassTestDraw
{
publicstaticvoidmain(String[]args)
{
//创建一个账户
Accountacct=newAccount("1234567",0);
newDrawThread("取钱者",acct,800).start();
newDepositThread("存钱者甲",acct,800).start();
newDepositThread("存钱者乙",acct,800).start();
newDepositThread("存钱者丙",acct,800).start();
}
}
运行结果:
取钱者进入封锁区。。。。。。。。
存钱者甲进入封锁区。。。。。。。。
存钱者甲存款:800.0
账户余额为:800.0
存钱者甲释放锁。。。。
存钱者丙进入封锁区。。。。。。。。
存钱者甲存钱结束!
存钱者丙等待。。。。。。
存钱者乙进入封锁区。。。。。。。。
存钱者乙等待。。。。。。
释放了
存钱者甲进入封锁区。。。。。。。。
存钱者甲等待。。。。。。
取钱者进入封锁区。。。。。。。。
取钱者取钱:800.0
账户余额为:0.0
释放了
取钱者进入封锁区。。。。。。。。
这里结果只粘贴了一部分。。。。聪明的你会发现这个程序最后阻塞啦,注意是阻塞不是死锁!阻塞的原因是:三个存钱的线程都运行结束了,但是取钱的线程还没有,所以阻塞啦。
总结
以上就是本文关于Java编程中实现Condition控制线程通信的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:
Java线程之锁对象Lock-同步问题更完美的处理方式代码实例
Java线程之线程同步synchronized和volatile详解
创建并运行一个java线程方法介绍
有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!