0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题 (2)

原代码用的if-else对flag进行判断,这里存在问题,直接导致不论存款(或取款)成功或失败(即wait),run()方法的循环计数器都会自增1,导致存款(或取款)次数比预计的少,进而导致存款(取款线程已执行完,而存款线程仍在执行)或取款(存款线程已执行完,而取款线程仍在执行)线程阻塞

应当采用while进行循环判断,线程被唤醒之后,应再次进行判断,而不是直接将循环计数器自增,可以保证在每个循环中都成功进行了一次存款

调用Condition对象的的await()、signal()、signalAll()方法实现线程间通信

上面Object的wait()、notify()、notifyAll()方法只能适用于this、显式的Object对象

对于用Lock进行加锁的同步方法,上面的三个方法则不适用,这时候得靠Condition对象的另外三个方法

通过Lock锁的newCondition()方法返回一个Condition对象,然后调用该对象的下面三个方法进行通信

await()

类似于wait()方法

await(long timeout,int nanos)

awaitnanos(long nanosTimeout)

awaitUninterruptibly()

awaitUntil(Date deadline)

signal()

类似于notify()

signalAll()

类似于notifyAll()

Lock锁的newCondition()方法返回的是ConditionObject对象,这是AbstractQueuedSynchronizer抽象类的一个内部类,该内部类实现了Condition接口

下面用Lock及这三个新方法改写上面的Account类

class Account { private String accountNO; private double balance; private boolean flag=false; private final ReentrantLock lock=new ReentrantLock(); //创建一把Lock锁 private final Condition cond=lock.newCondition(); //返回Condition对象 public Account(){} public Account(String no,double balance){ accountNO=no; this.balance=balance; } public double getBalance(){ return balance; } public void withdraw(double amount){ lock.lock(); //获取锁并加锁 try { while (!flag){ cond.await(); //调用Condition对象的await()方法 } System.out.println(Thread.currentThread().getName()+"取款:"+amount); balance-=amount; System.out.println("取款后,余额为: "+balance); flag=false; System.out.println("---------------上面取款完毕-------------------"); cond.signalAll(); }catch(InterruptedException ex){ ex.printStackTrace(); }finally{ lock.unlock(); //释放锁 } } public void deposit(double amount){ lock.lock(); try{ while (flag){ cond.await(); } System.out.println(Thread.currentThread().getName()+"存款"+amount); balance+=amount; System.out.println("存款后,账户余额为: "+balance); flag=true; System.out.println("---------------上面存款完毕-------------------"); cond.signalAll(); }catch(InterruptedException ex){ ex.printStackTrace(); }finally{ lock.unlock(); } } } 如果调用了Lock对象的wait()、notify()、notifyAll()方法会怎样?

Lock对象也是Object的子类的实例,也拥有这三个方法,按理说调用Lock对象这个同步监视器的该三个方法,也应该能达到通信的目的

改写后,程序输出如下:

存款者A存款325.0Exception in thread "存款者A" Exception in thread "取款者甲" //
存款后,账户余额为: 325.0
---------------上面存款完毕-------------------
取款者甲取款:325.0
取款后,余额为: 0.0
---------------上面取款完毕-------------------
存款者B存款325.0
存款后,账户余额为: 325.0
---------------上面存款完毕-------------------
Exception in thread "存款者B" 取款者乙取款:325.0
取款后,余额为: 0.0
java.lang.IllegalMonitorStateException
---------------上面取款完毕-------------------
at java.lang.Object.notifyAll(Native Method)
at testpack.Account.deposit(Test1.java:86)
at testpack.Deposit.run(Test1.java:39)
Exception in thread "取款者乙" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at testpack.Account.withdraw(Test1.java:68)
at testpack.Withdraw.run(Test1.java:25)
java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at testpack.Account.withdraw(Test1.java:68)
at testpack.Withdraw.run(Test1.java:25)
java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at testpack.Account.deposit(Test1.java:86)
at testpack.Deposit.run(Test1.java:39)

上面出现了大量的“IllegalMonitorStateException”异常,暂时还分析不了出错的原因

通过阻塞队列实现线程间通信

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzzgpj.html