java多线程notify wait简单实例,实现线程交互的wait()和notify()方法在类中定义

  java多线程notify wait简单实例,实现线程交互的wait()和notify()方法在类中定义

  00-1010 1.线程通信的定义2。为什么需要等待通知?3.wait方法和notify方法1,对象的wait()方法2,对象的notify()方法4。等待方法和通知方法5的原理。wait方法和notify方法的例子1、进入对象监视器的线程可以调用wait()方法2、进入对象监视器的线程可以调用notify()方法6。为什么在同步块中调用wait和notify方法?总结问题:

  1.使用thread wait()方法的前提是什么?

  2.多线程如何相互通信?

  3.Java中的notify和notifyAll有什么区别?

  4.为什么这些方法wait/notify/notifyAll不在thread类中?

  5.为什么在同步块中调用wait和notify方法?

  6.notify()和notifyAll()有什么区别?

  00-1010线程是操作系统调度的最小单位。它有自己的堆栈空间,可以按照既定的代码一步步执行。但是,如果每个线程都孤立运行,就会浪费资源。所以在现实中,如果多个线程需要按照指定的规则共同完成一项任务,那么这些线程就需要相互协调。这个过程称为线程通信。

  线程间的通信可以定义为:当多个线程联合操作共享资源时,线程以某种方式互相通知自己的状态,以避免无效的资源争用。

  线程间的通信有很多种方式:等待通知、共享内存、管道流。“等待并通知”通信模式是Java中常用的线程间通信模式,其经典案例是“生产者-消费者”模式。

  00-1010场景:几个孩子想进房间,用算盘(CPU)做计算。老王(操作系统)用的是同步锁,这样只有一个孩子可以同时进入房间使用算盘,所以他们排队进入房间。

  (1)小南先拿到了锁,进了房间。但由于条件不满足(没有烟,没有工作),小南无法继续计算。但是如果小南一直占着锁,其他人就得一直堵着,效率太低了。

  (2)于是老王开了一个休息室(叫等待法),让小南在休息室等着(WaitSet)。此时开锁,其他人可以由老王随机安排进屋。

  (3)直到小M发完烟,喊【你的烟到了】(调用notify方法)

  (4)小南随后可以离开休息室,重新进入比赛锁队列。

  在java中,线程间通信的“wait-notify”方式是通过使用对象的wait()和notify()方法实现的。每个java对象都有wait()和notify()实例方法,wait()和notify()方法与对象的监视器密切相关。

  Wait()和notify()方法的数量超过两个。wait()和notify()方法不属于Thread类,而是属于java对象实例。

  

目录

java对象中的wait()和notify()方法就像是信号开关,用于等待方和通知方的交互。

 

  00-1010对象的wait()方法的主要作用是使当前线程阻塞,等待被唤醒。wait()方法与对象监视器密切相关。使用wait()方法时,必须将它放在同步块中。wait()方法的调用如下:

  public class Main { static final Object lock=new Object();公共静态void method1()抛出interrupted exception { synchronized(lock){ lock . wait();} } }Object类中的wait()方法有三个版本:

  (1)void wait():当前线程调用同步对象锁。

  e>的wait()实例方法后,将导致当前的线程等待,当前线程进入lock的监视器WaitSet,等待被其他线程唤醒;

  (2)void wait(long timeout):限时等待。导致当前的线程等待,等待被其他线程唤醒,或者指定的时间timeout用完,线程不再等待;

  (3)void wait(long timeout,int nanos):高精度限时等待,其主要作用是更精确地控制等待时间。参数nanos是一个附加的纳秒级别的等待时间;

  

 

  

2、对象的notify()方法

对象的notify()方法的主要作用是唤醒在等待的线程。notify()方法与对象监视器紧密相关,调用notify()方法时也需要放在同步块中。notify()方法的调用方法如下:

 

  

public class Main { static final Object lock = new Object(); public static void method1() throws InterruptedException { synchronized( lock ) { lock.notify(); } }}

notify()方法有两个版本:

 

  (1)void notify()lock.notify()调用后,唤醒lock监视器等待集中的第一条等待线程;被唤醒的线程进入EntryList,其状态从WAITING变成BLOCKED。

  (2)void notifyAll()lock.notifyAll()被调用后,唤醒lock监视器等待集中的全部等待线程,所有被唤醒的线程进入EntryList,线程状态从WAITING变成BLOCKED。

  小结:

  obj.wait():让进入Object监视器的线程到waitset等待

  obj.notify():在Object上正在waitset等待的线程中挑一个唤醒

  obj.notifyAll():让在Object上正在waitset等待的线程全部唤醒

  

 

  

4. wait方法和notify方法的原理

对象的wait()方法的核心原理大致如下:

 

  (1) 当线程调用了lock(某个同步锁对象)的wait()方法后,jvm会将当前线程加入lock监视器的WaitSet(等待集),等待被其他线程唤醒。

  (2) 当前线程会释放lock对象监视器的Owner权利,让其他线程可以抢夺lock对象的监视器。

  (3) 让当前线程等待,其状态变成WAITING。在线程调用了同步对象lock的wait()方法之后,同步对象lock的监视器内部状态大致如图2-15所示。

  对象的notify()或者notifyAll()方法的原理大致如下:

  (1) 当线程调用了lock(某个同步锁对象)的notify()方法后,jvm会唤醒lock监视器WaitSet中的第一条等待线程。

  (2) 当线程调用了locknotifyAll()方法后,jvm会唤醒lock监视器WaitSet中的所有等待线程。

  (3) 等待线程被唤醒后,会从监视器的WaitSet移动到EntryList,线程具备了排队抢夺监视器Owner权利的资格,其状态从WAITING变成BLOCKED。

  (4) EntryList中的线程抢夺到监视器的Owner权利之后,线程的状态从BLOCKED变成Runnable,具备重新执行的资格。

  

 

  (1) Owner 线程发现条件不满足,调用wait方法,即可进入WaitSet,变为 WAITING 状态 ;

  (2) BLOCKED 和WAITING 的线程都处于阻塞状态,不占用CPU时间片 ;

  (3) BLOCKED:线程会在Owner 线程释放锁时唤醒 ;

  (4) WAITING :线程会在Owner 线程调用notifynotifyAll时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入 EntryList 重新竞争;

  

 

  

5. wait方法和notify方法示例

 

  

1、进入Object监视器的线程才能调用wait()方法

小南并不能直接进入WaitSet休息室,而是获取锁进入房间后才能进入休息室,没有锁的话小南没法进入房间,更没法进入休息室。他进入休息室后就会释放锁,让其他线程竞争锁进入房间。

 

  

 

  

public class Main { static final Object lock = new Object(); public static void main(String[] args) { synchronized (lock){ try { // 只有成为了monitor对象的owner,即获得了对象锁之后,才有资格进入waitset等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }}

 

  

2、进入Object监视器的线程才能调用notify()方法

小M此时获取到了锁,进入了房间,并唤醒了在休息室中等待的小王,小M如果获取到锁进行房间时没有办法唤醒在休息室等待的小王的,因为此时小M在门外,小王根本听不到。

 

  

 

  使用notify()唤醒等待区的一个线程:

  

public class Main { static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ System.out.println("t1线程开始执行..."); synchronized (lock){ try { // 让t1线程在lock锁的waitset中等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 被唤醒后,继续执行 System.out.println("线程t1被唤醒..."); } },"t1"); t1.start(); Thread t2 = new Thread(()->{ System.out.println("t2线程开始执行..."); synchronized (lock){ try { // 让t2线程在lock锁的waitset中等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 被唤醒后,继续执行 System.out.println("线程t2被唤醒..."); },"t2"); t2.start(); Thread.sleep(2000); System.out.println("唤醒lock锁上等待的线程..."); synchronized (lock){ // 主线程拿到锁后,唤醒正在休息室中等待的某一个线程 lock.notify(); } }}

执行结果:

 

  

t1线程开始执行...t2线程开始执行...唤醒lock锁上等待的线程...线程t1被唤醒...

 

  

使用notifyAll()唤醒等待区所有的线程:

 

  

public class Main { static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ System.out.println("t1线程开始执行..."); synchronized (lock){ try { // 让t1线程在lock锁的waitset中等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 被唤醒后,继续执行 System.out.println("线程t1被唤醒..."); } },"t1"); t1.start(); Thread t2 = new Thread(()->{ System.out.println("t2线程开始执行..."); synchronized (lock){ try { // 让t2线程在lock锁的waitset中等待 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 被唤醒后,继续执行 System.out.println("线程t2被唤醒..."); },"t2"); t2.start(); Thread.sleep(2000); System.out.println("唤醒lock锁上等待的线程..."); synchronized (lock){ // 主线程拿到锁后,唤醒正在休息室中等待的所有线程 lock.notifyAll(); } }}

执行结果:

 

  

t1线程开始执行...t2线程开始执行...唤醒lock锁上等待的线程...线程t2被唤醒...线程t1被唤醒...

 

  

 

  

6. 为什么 wait 和 notify 方法要在同步块中调用?

在调用同步对象的wait()和notify()系列方法时,当前线程必须拥有该对象的同步锁,也就是说,wait()和notify()系列方法需要在同步块中使用,否则JVM会抛出类似如下的异常:

 

  

 

  为什么wait和notify不在synchronized同步块的内部使用会抛出异常呢?这需要从wait()和notify()方法的原理说起。

  wait()方法的原理:

  首先,JVM会释放当前线程的对象锁监视器的Owner资格;其次,JVM会将当前线程移入监视器的WaitSet队列,而这些操作都和对象锁监视器是相关的。所以,wait()方法必须在synchronized同步块的内部调用。在当前线程执行wait()方法前,必须通过synchronized()方法成为对象锁的监视器的Owner。

  notify()方法的原理:

  JVM从对象锁的监视器的WaitSet队列移动一个线程到其EntryList队列,这些操作都与对象锁的监视器有关。所以,notify()方法也必须在synchronized同步块的内部调用。在执行notify()方法前,当前线程也必须通过synchronized()方法成为对象锁的监视器的Owner。

  

 

  

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注盛行IT的更多内容!

 

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: