疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java多线程通信 详解与举例


 

   

  1.使用Object类的方法来实现线程之间的通信

  为了实现线程通信,我们可以使用Object类提供的wait()、notify()、notifyAll()三个方法。调用wait()方法会释放对该同步监视器的锁定。这三个方法必须由同步监视器对象来调用,这可分成两种情况:

  对于使用synchronized修饰的同步方法,因为该类的默认实例是(this)就是同步监视器,所以可以直接调用这三使用个方法。

  对于synchronized修饰的同步代码块,同步监视器是synchronized括号里的对象,所以必须使用该对象调用这三个方法。

  这次我们还是拿银行账户的取钱、存款来作为本文的例子演示。

  假设系统中有两条线程,这两条线程分别代表取钱者和存钱者。现在系统有一种特殊的要求,系统要求存款者和取钱者不断的实现存款和取钱动作,而且要求每当存款者将钱存入指定账户后,取钱者立即将钱取走.不允许存款者两次存钱,也不允许取钱者两次取钱.

  我们通过设置一个旗标来标识账户中是否已有存款,有就为true,没有就标为false。具体代码如下:

  首先我们定义一个Account类,这个类中有取钱和存钱的两个方法,由于这两个方法可能需要并发的执行取钱、存钱操作,所有将这两个方法都修改为同步方法.(使用synchronized关键字)。

  [java] view plain copy

  public class Account {

  private String accountNo;

  private double balance;

  //标识账户中是否有存款的旗标

  private boolean flag=false;

  public Account() {

  super();

  }

  public Account(String accountNo, double balance) {

  super();

  this.accountNo = accountNo;

  this.balance = balance;

  }

  public synchronized void draw (double drawAmount){

  try {

  if(!flag){

  this.wait();

  }else {

  //取钱

  System.out.println(Thread.currentThread().getName()+" 取钱:"+drawAmount);

  balance=balance-drawAmount;

  System.out.println("余额 : "+balance);

  //将标识账户是否已有存款的标志设为false

  flag=false;

  //唤醒其它线程

  this.notifyAll();

  }

  } catch (Exception e) {

  e.printStackTrace();

  }

  }

  public synchronized void deposit(double depositAmount){

  try {

  if(flag){

  this.wait();

  }

  else{

  System.out.println(Thread.currentThread().getName()+"存钱"+depositAmount);

  balance=balance+depositAmount;

  System.out.println("账户余额为:"+balance);

  flag=true;

  //唤醒其它线程

  this.notifyAll();

  }

  } catch (Exception e) {

  // TODO: handle exception

  e.printStackTrace();

  }

  }

  }

  接下来创建两个线程类,分别为取钱和存钱线程!

  取钱线程类:

  [java] view plain copy

  public class DrawThread implements Runnable {

  private Account account;

  private double drawAmount;

  public DrawThread(Account account, double drawAmount) {

  super();

  this.account = account;

  this.drawAmount = drawAmount;

  }

  public void run() {

  for(int i=0;i<100;i++){

  account.draw(drawAmount);

  }

  }

  }

  存钱线程类:

  [java] view plain copy

  public class depositThread implements Runnable{

  private Account account;

  private double depositAmount;

  public depositThread(Account account, double depositAmount) {

  super();

  this.account = account;

  this.depositAmount = depositAmount;

  }

  public void run() {

  for(int i=0;i<100;i++){

  account.deposit(depositAmount);

  }

  }

  }

  最后我们测试一下这个取钱和存钱的操作!

  [java] view plain copy

  public class TestDraw {

  public static void main(String[] args) {

  //创建一个账户

  Account account=new Account();

  new Thread(new DrawThread(account, 800),"取钱者").start();

  new Thread(new depositThread(account, 800),"存款者甲").start();

  new Thread(new depositThread(account, 800),"存款者乙").start();

  new Thread(new depositThread(account, 800),"存款者丙").start();

  }

  }

  大致的输出结果:

  [java] view plain copy

  存款者甲存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  存款者丙存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  存款者甲存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  存款者丙存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  存款者甲存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  存款者丙存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  存款者甲存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  存款者丙存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  存款者甲存钱800.0

  账户余额为:800.0

  取钱者 取钱:800.0

  余额 : 0.0

  由上述结果我们可以看到,存款者线程和取钱者线程交替执行的情形。至此简单的线程通信问题已经模拟完毕!

  2.使用java.util.concurrent.locks下的Condition类来实现线程间的通信

  如何程序不使用synchronized关键字来保持同步,而是直接适用Lock对像来保持同步,则系统中不存在隐式的同步监视器对象,也就不能使用wait()、notify()、notifyAll()来协调线程的运行.

  当使用LOCK对象保持同步时,JAVA为我们提供了Condition类来协调线程的运行。关于Condition类,JDK文档里进行了详细的解释.,再次就不啰嗦了。

  我们就拿Account类进行稍微的修改 一下吧!

  [java] view plain copy

  import java.util.concurrent.locks.Condition;

  import java.util.concurrent.locks.Lock;

  import java.util.concurrent.locks.ReentrantLock;

  public class Account {

  //显示定义Lock对象

  private final Lock lock=new ReentrantLock();

  //获得指定Lock对象对应的条件变量

  private final Condition con=lock.newCondition();

  private String accountNo;

  private double balance;

  //标识账户中是否有存款的旗标

  private boolean flag=false;

  public Account() {

  super();

  }

  public Account(String accountNo, double balance) {

  super();

  this.accountNo = accountNo;

  this.balance = balance;

  }

  public void draw (double drawAmount){

  //加锁

  lock.lock();

  try {

  if(!flag){

  // this.wait();

  con.await();

  }else {

  //取钱

  System.out.println(Thread.currentThread().getName()+" 取钱:"+drawAmount);

  balance=balance-drawAmount;

  System.out.println("余额 : "+balance);

  //将标识账户是否已有存款的标志设为false

  flag=false;

  //唤醒其它线程

  // this.notifyAll();

  con.signalAll();

  }

  } catch (Exception e) {

  e.printStackTrace();

  }

  finally{

  lock.unlock();

  }

  }

  public void deposit(double depositAmount){

  //加锁

  lock.lock();

  try {

  if(flag){

  // this.wait();

  con.await();

  }

  else{

  System.out.println(Thread.currentThread().getName()+"存钱"+depositAmount);

  balance=balance+depositAmount;

  System.out.println("账户余额为:"+balance);

  flag=true;

  //唤醒其它线程

  // this.notifyAll();

  con.signalAll();

  }

  } catch (Exception e) {

  // TODO: handle exception

  e.printStackTrace();

  }finally{

  lock.unlock();

  }

  }

  }

  输出结果和上面是一样的! 只不过这里 显示的使用Lock对像来充当同步监视器,使用Condition对象来暂停指定线程,唤醒指定线程!