Java多线程协调控制之wait&notifyAll

来源:互联网 发布:刺马案 知乎 编辑:程序博客网 时间:2024/05/29 09:42

最近在复习Java基础,看到多线程这块顺便写写多线程的协调控制程序。


需求:假设系统中有两个线程分别代表取款者和存款者,现在系统的要求是存款者和取款者不断的重复存、取款操作,

并且要求每当有存款者将钱存入指定账户中时,取款者就立即取出这笔钱,即不允许存款者连续两次存钱,也不允许

取款者两次取钱。


下面代码实现:

1.首先是账户Account类;

package com.xjtu.cruise.soft.thread;public class Account {/** * @author Cruise * @param args */private String accountNo;//标识账户是否还有存款的private boolean flag = false;private double balance;public Account(){}public Account(String accountNo, double balance) {this.accountNo = accountNo;this.balance = balance;}public double getBalance(){return balance;}public synchronized void draw(double drawAmount) {try {if (!flag) {  //如果账户没钱wait();  //阻塞当前的方法,并释放同步锁(这里就是this,也就是调用此方法的对象)} else {System.out.println("线程:" + Thread.currentThread().getName()+ "取款" + drawAmount);balance -= drawAmount;System.out.println("账户余额:" + balance);flag = false;notifyAll();  //唤醒等待此同步锁的所有线程}} catch (InterruptedException e) {e.printStackTrace();}}public synchronized void deposit(double depositAmount) {try{if(flag){ //如果账户有存入的钱wait();}else{System.out.println("线程:"+ Thread.currentThread().getName()+"存款"+depositAmount);balance += depositAmount;System.out.println("账户余额:" + balance);flag = true;notifyAll();}}catch(InterruptedException e){e.printStackTrace();}}}


2.接下来是存取款线程类(DrawThread,DepositThread)

package com.xjtu.cruise.soft.thread;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<100; i++){//System.out.println("线程:" + Thread.currentThread().getName()+"执行存款操作。");account.deposit(depositAmount);}}}

package com.xjtu.cruise.soft.thread;public class DrawThread extends Thread {/**     * @author Cruise     * @param args     */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<100; i++){//System.out.println("线程:" + Thread.currentThread().getName()+"执行取钱操作。");account.draw(drawAmount);}}}

3.奉上测试类TestDraw

package com.xjtu.cruise.soft.thread;public class TestDraw {/** * @author Cruise * @param args */public static void main(String[] args) {Account account = new Account("123", 0);new DrawThread("取钱者", account, 800).start();new DepositThread("存款者甲",account, 800).start();new DepositThread("存款者乙",account, 800).start();}}

OK, 一个使用wait和notifyAll控制线程协调的Demo完成,运行结果如下:


下面再使用条件变量的方式来协调线程的同步,具体来说就是使用Lock对象来保证同步,而不是用Synchronized关键字。

通过Lock实例调用其newCondition()方法得到对应的Condition对象,接着使用此Condition实例的方法来保证线程的协

调。这里用到了Condition的await和signalAll方法。

将上面的Account类修改如下,其他的线程类不变。

package com.xjtu.cruise.soft.thread;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Account {/** * @author Cruise * @param args */private final Lock lock = new ReentrantLock();//定义Lock对象private final Condition cond = lock.newCondition();//获取Lock对象对应的条件变量private String accountNo;//标识账户是否还有存款的private boolean flag = false;private double balance;public Account(){}public Account(String accountNo, double balance) {this.accountNo = accountNo;this.balance = balance;}public double getBalance(){return balance;}public void draw(double drawAmount) {lock.lock();//加锁try {if (!flag) {  //如果账户没钱cond.await();  } else {System.out.println("线程:" + Thread.currentThread().getName()+ "取款" + drawAmount);balance -= drawAmount;System.out.println("账户余额:" + balance);flag = false;cond.signalAll();  //唤醒该Lock对象对应的其他线程}} catch (InterruptedException e) {e.printStackTrace();}finally{lock.unlock();//使用finally块确保释放锁}}public void deposit(double depositAmount) {lock.lock();//加锁try{if(flag){ //如果账户有存入的钱cond.await();}else{System.out.println("线程:"+ Thread.currentThread().getName()+"存款"+depositAmount);balance += depositAmount;System.out.println("账户余额:" + balance);flag = true;cond.signalAll();//唤醒该Lock对象对应的其他线程}}catch(InterruptedException e){e.printStackTrace();}finally{//使用finally块确保释放锁lock.unlock();}}}

运行上面的测试类,可以得到如下的运行结果:

从上面的两个运行结果可以发现,虽然两种方法都可以保证线程的同步和协调运行,但是线程的调度机制还是有些许的

差别,这点可以从上图的红框看出。当然底层的具体实现,因为不大了解,这里就不作具体的阐述了(废话,不了解当

然阐述不了,^_^),希望了解的同学能帮忙解答下,very thx!


0 0