线程通讯 wait() 与 notify()

总结

  1. 什么是线程通讯,可以将线程分为生产者线程与消费者线程,生产者线程创建共享数据(写),给消费者线程使用(读),注意是生产一个消费一个
  2. wait() 方法: 当调用该方法时当前线程进入等待,并释放锁
  3. notify() 方法: 当调用该方法时,唤醒当前对象锁池中等待的线程, 注意点: wait()与notify()方法一定要在Synchronized方法中执行,并且持有的是同一把锁
  4. wait()与join()的区别:wait()用在 Synchronized 中, 被wait()的线程需要唤醒,
  5. wait()与sleep()的区别,sleep指定休眠时间,时间过后自动唤醒,并且sleep不会释放锁

使用 wait() 与 notify() 实现线程通讯的原理

  • 生产线程与消费线程持有同一把锁,在一个锁池中
  • 生产消费的数据中有代表是否生产完毕消费完毕的标识.
  • 生产线程在执行时首先判断当前数据是否有消费,如果以消费,则执行生产方法进行生产,当生产完毕时修改标识,调用notify()方法当前线程释放锁资源,同一个锁池中的线程再次争抢,如果未消费完毕则调用wait(),当前线程释放锁资源进入等待状态
  • 与生成线程相同,消费线程通过标识首先判断是否生产完毕,如果生产完毕执行消费方法,消费完毕后修改标识调用notify()释放锁资源…

线程通讯示例

  1. 创建生产消费的数据类
class Res2 {
    public String userSex;//性别
    public String userName;//年龄
    //线程通讯标识(生成与消费线程,通过判断这个标识来确定
    //接下来的执行,是该生产消息还是消费消息
    public boolean flag = false;
}
  1. 创建生产线程
class IntThrad extends Thread {
    private Res2 res2;//设置Res2位线程类的私有变量
    /*初始化生产线程对象时需要传递Res2对象,在
    * 生成线程类中的run方法中需要调用Res2中的方法,
    * 进而对Res2的这个方法进行多线程处理*/
    public IntThrad(Res2 res2) {
        this.res2 = res2;
    }
    /*run方法,在run方法中通过Res2对象,对该对象的属性进行赋值*/
    @Override
    public void run() {
        int count = 0;
        while (true) {
            synchronized (res2) {
                /*获取res2中flag,如果flag为true时,代表刚刚设置了
                * res2中的属性值,需要消费线程先进行消费,
                * 通过同步代码块中的锁对象,调用wait()方法,
                * 当前生产线程释放锁进入,锁池中等待*/
                if (res2.flag) {
                    try {
                        // 当前线程变为等待,但是可以释放锁
                        res2.wait();
                    } catch (Exception e) {

                    }
                }
                /*如果falg为false,则是消费线程刚进行了消费,
                * 现在要进行生产,通过传入的res2对象对其属性进行赋值
                * 如果count为0,则res2是....否则是...*/
                if (count == 0) {
                    res2.userName = "AAA";
                    res2.userSex = "男";
                } else {
                    res2.userName = "BBB";
                    res2.userSex = "女";
                }
                /*count+1然后除以2,永远时两种结果*/
                count = (count + 1) % 2;
                /*设置flag的值为true,通过这个值判断接下来的操作时生产
                * 还是消费,生产是false,消费时true,刚刚生产完,需要进行
                * 消费,设置为true*/
                res2.flag = true;
                // 消息生产完成后,通过锁对象调用notify方法,
                // 唤醒一个当前锁池中的一个等待线程,两个线程
                //再次通过cpu判断谁先执行
                res2.notify();
            }
        }
    }
}

  1. 创建消费线程
class OutThread extends Thread {
    /*将Res2对象设置为消费线程类的私有变量*/
    private Res2 res2;
    /*在初始化消费线程类对象时,需要传递一个Res2对象
    * 在线程类的run方法中通过Res2对象,调用它的方法
    * 进而对Res2中的方法实现多线程操作*/
    public OutThread(Res2 res2) {
        this.res2 = res2;
    }
    /*消费run方法,方法中调用了Res2中的方法*/
    @Override
    public void run() {
        while (true) {
            synchronized (res2) {
                /*获取res2中的flag判断,如果不是true,说明
                * 刚刚消费完,接下来的操作需要进行生产,通过
                * 当前线程的锁对象调用wait()方法,当前消费线程
                * 释放锁,进入锁池中等待*/
                if (!res2.flag) {
                    try {
                        res2.wait();
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                }
                /*如果flag为true,说明刚刚生产完,需要进行消费,获取res2中的属性值进行消费*/
                System.out.println(res2.userName + "--" + res2.userSex);
                /*消费完成后设置flag标志为刚刚消费完,接下来要进行生产*/
                res2.flag = false;
                /*并通过当前锁对象,调用notify()方法,唤醒当前锁池中
                * 的一个等待线程,两个线程再次进行资源争抢*/
                res2.notify();
            }
        }
    }
}
  1. 通讯执行测试
/*线程通信示例
* 注意点:
* 1.两个线程是通过同一个Res2对象创建的
* 2.生产线程与消费线程操作的共享数据要实现同步synchronized
* 3.实现synchronized的两个线程要共用同一把锁
* 4.生产消息与消费消息要生产一个消费一个(不能批量生产,批量消费)
* 5.notify与wait方法要在synchronized中配合使用,并需要同一把锁对象调用
* 6.wait方法,当前线程释放锁进入锁池中等待,再次启用这个线程需要被同一把锁对象调用notify唤醒
* 7.在Res2中声明一个flag属性,通过这个属性判断接下来的操作是该生成还是消费,进而选择当前线程
*   是继续执行还是wait
* 8.虽然消费线程是对共享数据进行读的操作,但是也要实现synchronized,不然会发送错误* */
public class ThreaCommun {
    public static void main(String[] args) {
        Res2 res2 = new Res2();//创建Res2对象,传递给生产线程对象与消费线程对象
        IntThrad intThrad = new IntThrad(res2);//创建生产线程
        OutThread outThread = new OutThread(res2);//创建消费线程
        intThrad.start();//启动生产线程
        outThread.start();//启动消费线程
    }
}

使用 Condition 设置线程通讯

  1. 创建数据类
class Res3 {//共享数据
    public String userName;
   public String sex;
   public boolean flag = false;//执行生产或消费的标识
   Lock lock = new ReentrantLock();
}

  1. 创建生产线程
class InputThread extends Thread {//继承方式创建生产线程类
   private Res3 res3;//Res3对象属性
   Condition newCondition;//Condition对象属性
   //初始化线程类,需要传递Res3对象,与Condition对象
   //在线程类的run方法中会使用到两个对象中的成员
   public InputThread(Res3 res3,  Condition newCondition) {
      this.res3 = res3;
      this.newCondition=newCondition;
   }
   @Override
   public void run() {//run方法中设置共享数据生产
      int count = 0;
      while (true) {
         // synchronized (res3) {
         try {
            res3.lock.lock();
            if (res3.flag) {
               try {
//                res3.wait();
                  newCondition.await();//通过Condition调用await()使当前线程等待
               } catch (Exception e) {
                             }
            }
            if (count == 0) {
               res3.userName = "AAA";
               res3.sex = "男";
            } else {
               res3.userName = "小红";
               res3.sex = "女";
            }
            count = (count + 1) % 2;
            res3.flag = true;//修改进程生产还是消费操作的标识
//          res3.notify();
            newCondition.signal();//通过Condition调用signal(),唤醒一个锁池中的等待线程
         } catch (Exception e) {
         }finally {
            res3.lock.unlock();//需要在finally中手动释放锁
         }
      }
      // }
   }
}

  1. 创建消费线程
class OutThrad extends Thread {//继承方式创建消费线程类
   private Res3 res3;
   private Condition newCondition;
   public OutThrad(Res3 res3,Condition newCondition) {
      this.res3 = res3;
      this.newCondition=newCondition;
   }
   @Override
   public void run() {//run方法中获取共享数据消费
      while (true) {
//       synchronized (res3) {
         try {
            res3.lock.lock();
            if (!res3.flag) {
               try {
//                res3.wait();
                  newCondition.await();
               } catch (Exception e) {
                  // TODO: handle exception
               }
            }
            System.out.println(res3.userName + "," + res3.sex);
            res3.flag = false;//修改标识
//          res3.notify();
            newCondition.signal();//唤醒锁池中一个等待线程
         } catch (Exception e) {
            // TODO: handle exception
         }finally {
            res3.lock.unlock();//需要在finally中手动释放锁
         }
//       }
      }
   }
}

  1. 执行生产与消费
public class ThreadDemo01 {
   public static void main(String[] args) {
      Res3 res3 = new Res3();
      Condition newCondition = res3.lock.newCondition();
      InputThread inputThread = new InputThread(res3,newCondition);
      OutThrad outThrad = new OutThrad(res3,newCondition);
      inputThread.start();
      outThrad.start();
   }
}

发布了48 篇原创文章 · 获赞 0 · 访问量 573

猜你喜欢

转载自blog.csdn.net/qq_29799655/article/details/105524735