精华内容
下载资源
问答
  • Object的wait方法

    2020-08-05 17:00:00
    当线程B访问某个共享资源时,想获取资源锁对象,发现这个锁已经被线程A拿到了,这个时候,线程B只能被挂起,等待线程A释放锁。 但是拿到锁线程A在执行过程中,因为某些条件还不满足,暂时不想继续执行下去, ...
    当线程B访问某个共享资源时,想获取资源的锁对象,发现这个锁已经被线程A拿到了,这个时候,线程B只能被挂起,等待线程A释放锁。
    但是拿到锁的线程A在执行的过程中,因为某些条件还不满足,暂时不想继续执行下去,
    想先等待一下(注意:是已经拿到锁的线程A自己想主动等待的),希望等到某个条件满足后,继续执行任务。
    在同步代码块里,线程A必须先释放锁,线程B才有资格获取锁,进入同步代码块,执行代码。
    等线程B执行完后,线程A需要的条件已经满足,那么这个时候必须有一个通知机制,让线程A从等待状态变成执行状态,继续执行代码。
    展开全文
  • 概述 wait和notify是用于线程间通信。...一般wait的用法如下 while(queue.size() == MAX_SIZE){ wait() } 假如不对这段代码加锁,就会出现问题。模拟一个生产者线程t1和一个消费者线程t2,例子如下 t...

    概述

    wait和notify是用于线程间通信。
    以生产者消费者模式来说的话,就是生产者和消费者通过队列进行通信,既然他们通过同一个队列通信,那么对于队列的操作就一定要保证线程安全性。

    代码解释

    一般wait的用法如下

    while(queue.size() == MAX_SIZE){
    	wait()
    }
    

    假如不对这段代码加锁,就会出现问题。模拟一个生产者线程t1和一个消费者线程t2,例子如下

    • t1判断队列满,需要wait阻塞线程。
    • 但是就在t1还没有调用wait的时候,消费者t2消费了一个产品,导致队列非满。
    • 这时候生产者线程t1调用wait阻塞,造成的情况就是队列非满,但是生产者线程阻塞了。
    • 假如此时消费者不消费了,那么生产者则会一直阻塞下去。。。出现老问题!!!

    所以在调用wait和notify以及notifyAll等方法时一定要进行同步处理。

    温馨提示

    • 如果您对本文有疑问,请在评论部分留言,我会在最短时间回复。
    • 如果本文帮助了您,也请评论,作为对我的一份鼓励。
    • 如果您感觉我写的有问题,也请批评指正,我会尽量修改。
    展开全文
  • 文章目录1.生产者和消费者模型2. 死锁产生3.解决问题4.notify和notifyAll区别5....在介绍完前文synchronized关键字基本使用之后,本文来对这些方法进行分析。 1.生产者和消费者模型 Producer代码如下: public class


    还记得前面用ArrayList实现阻塞队列的文章:《什么?面试官让我用ArrayList实现一个阻塞队列?》。我们通过synchronized并配合wait和notify实现了一个阻塞队列。在介绍完前文的synchronized关键字的基本使用之后,本文来对这些方法进行分析。

    1.生产者和消费者模型

    Producer代码如下:

    public class Producer implements Runnable {
    
    	List<Integer> cache;
    
    	public Producer(List<Integer> cache) {
    		new Object();
    		this.cache = cache;
    	}
    
    	public void put() throws InterruptedException {
    		synchronized (cache) {
    			System.out.println(Thread.currentThread().getName()+"获得锁");
    			cache.notify();
    			while (cache.size() == 1) {
    				try {
    					System.out.println(Thread.currentThread().getName()+"wait");
    					cache.wait();
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    
    			TimeUnit.SECONDS.sleep(1);
    			cache.add(1);
    			System.out.println(Thread.currentThread().getName() + "生产者写入1条消息");
    		}
    
    	}
    
    
    	@Override
    	public void run() {
    		while (true) {
    			try {
    				put();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    Consumer代码如下:

    
    public class Consumer implements Runnable {
    
    
    	List<Integer> cache;
    
    	public Consumer(List<Integer> cache) {
    		this.cache = cache;
    	}
    
    	private void consumer() {
    		synchronized (cache) {
    			System.out.println(Thread.currentThread().getName()+"获得锁");
    			cache.notify();
    			while (cache.size() == 0) {
    				try {
    					System.out.println(Thread.currentThread().getName()+"wait");
    					cache.wait();
    				}catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    
    			cache.remove(0);
    			System.out.println(Thread.currentThread().getName()+" 消费者消费了1条消息");
    		}
    	}
    
    	@Override
    	public void run() {
    		while (true) {
    			consumer();
    		}
    	}
    }
    

    我们来调用上述的生产者和消费者模型:

    public static void main(String[] args) {
    	List<Integer> cache = new ArrayList<>();
    	new Thread(new Producer(cache),"P1").start();
    	new Thread(new Consumer(cache),"C1").start();
    }
    

    启用了两个线程,分别调用生产者和消费者,可以看到这个过程交替执行:

    P1获得锁
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1获得锁
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    

    只要生产者产生了数据,那么消费者就能进行消费。

    2. 死锁产生

    还是利用上述代码,我们来增加一个消费者:

    public static void main(String[] args) {
    	List<Integer> cache = new ArrayList<>();
    	new Thread(new Producer(cache),"P1").start();
    	new Thread(new Consumer(cache),"C1").start();
    	new Thread(new Consumer(cache),"C2").start();
    }
    

    我们发现程序执行了一段时间之后都停止不动了:

    P1获得锁
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C2获得锁
    C2 消费者消费了1条消息
    C2获得锁
    C2wait
    C1获得锁
    C1wait
    C2wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    C2wait
    

    在IDEA中,我们对这三个线程的状态dump了进行查看:

    "P1" #12 prio=5 os_prio=0 tid=0x0000000020c20800 nid=0x1e9c in Object.wait() [0x00000000219fe000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x000000076ba2eae0> (a java.util.ArrayList)
    	at java.lang.Object.wait(Object.java:502)
    	at com.dhb.notify.Producer.put(Producer.java:22)
    	- locked <0x000000076ba2eae0> (a java.util.ArrayList)
    	at com.dhb.notify.Producer.run(Producer.java:40)
    	at java.lang.Thread.run(Thread.java:748)
    
    "C1" #13 prio=5 os_prio=0 tid=0x0000000020c2f000 nid=0x3448 in Object.wait() [0x0000000021aff000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x000000076ba2eae0> (a java.util.ArrayList)
    	at java.lang.Object.wait(Object.java:502)
    	at com.dhb.notify.Consumer.consumer(Consumer.java:21)
    	- locked <0x000000076ba2eae0> (a java.util.ArrayList)
    	at com.dhb.notify.Consumer.run(Consumer.java:35)
    	at java.lang.Thread.run(Thread.java:748)
    	
    "C2" #14 prio=5 os_prio=0 tid=0x0000000020c30000 nid=0x904 in Object.wait() [0x0000000021bff000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x000000076ba2eae0> (a java.util.ArrayList)
    	at java.lang.Object.wait(Object.java:502)
    	at com.dhb.notify.Consumer.consumer(Consumer.java:21)
    	- locked <0x000000076ba2eae0> (a java.util.ArrayList)
    	at com.dhb.notify.Consumer.run(Consumer.java:35)
    	at java.lang.Thread.run(Thread.java:748)
    

    可以发现,这三个线程,都是处于WATTING状态。
    这说明产生了死锁,没有人再去对线程进行唤醒操作。

    3.解决问题

    实际上,上述问题的解决方式很简单,只需要在Consumer和Producer中将notify方法都缓冲notifyAll方法就能解决。我们在后面章节来分析产生死锁的具体原因,在此先看看修改代码后的执行效果.
    将所有

    cache.notify();
    

    替换为:

    cache.notifyAll();
    

    执行效果:

    P1获得锁
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1获得锁
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    C2获得锁
    C2wait
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    C2wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C2 消费者消费了1条消息
    C2获得锁
    C2wait
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    C2wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C2 消费者消费了1条消息
    C2获得锁
    C2wait
    C1wait
    

    可以看到上述程序可以正常运行,没有任何死锁问题。

    4.notify和notifyAll区别

    这才是本文的关键知识点,通过前面这两个示例,相信大家都能看出来,notify会造成死锁,而notify则不会。这是因为,在前面我们分析过Object的源码,在注释中,就提到,notify只会选择等待队列其中之一的线程,将其变为阻塞状态,等待获得CPU的执行权。而NotifyAll则是将等待队列全部的线程都添加到等EntryList,然后这些线程都会等待获得CPU的执行时间。
    实际上,我们在前文分析Thread源码的时候,对线程的各自运行状态进行了总结:
    java线程状态模型

    在java中线程的运行状态共有6种。而wait则是将线程从RUNNING变为WAITING状态。而notify和notifyAll则是将线程从WAITING状态变为RUNNING的方法。实际上,首先这个转换过程需要再经过BLOCK状态转换。因为只有部分线程能获得锁,进入执行状态,而获得不到的,自然进入了BLOCK状态。
    notify只会选择等待队列的一个线程,这个过程是不确定的,由的jvm可能是随机选择,而hotspot再1.8种,直接是从WaitSet的头部拿到第一个线程。
    notify过程如下图所示:
    notify 过程

    而notifyAll则不同于notify,由于会将全部wait的线程都进入EntryList队列,这个过程如下:
    notifyAll

    这就是这两个方法的区别。

    5.死锁产生的原因

    5.1 两个线程

    首先看看两个线程的时候:

    P1获得锁
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1获得锁
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    

    这个过程日志可以用下图表示:

      1. 由于两个线程都启动了,P1首先获得锁,那么C1阻塞。状态为P1执行,C1阻塞。
        P1执行 C1阻塞
    • 2.再这之后,P1执行完调用wait方法,C1获得锁,那么此时C1执行,P1等待:
      C1执行 P1等待

    • 3.之后,C1调用notify方法将P1唤醒,由于C1还需要执行后续相关代码,那么此时P1进入阻塞队列
      C1 notify P1

    • 4.随后,C1执行完毕进入wait状态,这时候P1重写获得锁:
      P1执行 C1等待

    • 等P1执行的时候,又会再次notify C1
      P1 notify C1

    上述过程就会不断循环,这样线程就不会卡顿。会一直执行下去。

    5.2 三个线程

    那么现在换成三个线程,我们来看看日志:

    P1获得锁
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C2获得锁
    C2 消费者消费了1条消息
    C2获得锁
    C2wait
    C1获得锁
    C1wait
    C2wait
    P1生产者写入1条消息
    P1获得锁
    P1wait
    C1 消费者消费了1条消息
    C1获得锁
    C1wait
    C2wait
    

    上述过程我们用画图的方式来进行:

    • 1.P1首先获得锁,而C1、C2阻塞。P1虽然会调用notify但是waitSet没有线程。
      P1执行 C1C2阻塞

    • 2.之后C2获得了锁,此时P1等待,C1阻塞
      C2执行 P1等待 C1阻塞

    • 3.此后C2调用Notify方法,将P1变为阻塞状态。然后消费数据。此时cache长度为0。
      C2 notify P1

    • 4.此后C1获得锁,此时C2等待,P1阻塞。
      C1 执行,C2等待 P1阻塞

    • 5.此时C1调用notify将C2变为阻塞。此时while循环由于cache长度为0,因此C1会调用wait方法。
      C1 notify C2

    • 6.此后,P1再次获得锁。此时C1等待,C2执行。
      P1 执行,C1等待、C2阻塞

    • 7.从此时开始,每个线程的执行都需要注意了,此时线程再次获得锁的时候只会执行wait之后的代码。对于P1,由于cache的size为0,因此会继续写入数据。之后再次进入循环会发notify。将C1变为阻塞。
      P1 notify C1

    • 8.之后 C1获得锁,而P1等待,C2阻塞
      C1 执行,P1等待、C2阻塞

    • 9.C1再次获得锁,只会执行wait之后的内容:

    while (cache.size() == 0) {
    	try {
    		System.out.println(Thread.currentThread().getName()+"wait");
    		cache.wait();
    	}catch (InterruptedException e) {
    		e.printStackTrace();
    	}
    }
    

    可以看到这个代码,当从wait方法之后由于cache.size为0,再次进入wait状态,这里没办法调用notify了。
    C1while条件不满足继续wait

    • 10.之后C2也再次获得锁,但是与C1一样,由于条件不满足,不会执行notify。
      C2while条件不满足继续wait

    这样就对线程死锁进行了复盘。可以看到,notify导致问题的原因是,如果我们的条件没设置好,这会导致线程没办法去执行notify操作。而这样的话所有的线程都可能进入wait状态。再也没有人来调用notify,这就导致了死锁。悲剧就这样发生了。这也进一步说明,notify方法是非常脆弱的,如果我们的代码种在同一个锁上的竞争的线程只有2个的话,notify是完全能胜任的。但是如果超过2个,就会因为条件设置不合理而导致了死锁。
    而notify,则是无论什么时候,只要被调用,就会将所有的线程全部移动到阻塞队列等待锁。只要有一个线程调用notifyAll就能将所有的线程唤醒。

    5.总结

    本文对notify和notifyAll方法进行了分析,需要注意的是,notify和notifyAll方法的区别,一个是唤醒其中之一的等待线程,不同的JVM实现的方式不同,而HotSpot源码中,是从WaitSet中取的head元素。也就是说,谁先进入Wait状态则就会将谁notify出来。而notifyAll则是将全部的线程都从WaitSet取出。这样就不会有线程等待。通过上述分析可见,产生死锁的根本原因还是在条件变量的控制。
    但是需要注意的是,虽然从WaitSet拿到元素不一定随机。但是,多个线程对锁的竞争的情况确是不一定的。上述的生产者消费者模型也只能用于模拟演示,因为有可能,生产者线程可能一直抢不到锁,导致全部都是消费者线程互相争抢。

    展开全文
  • yield方法和wait方法都会导致当前线程停止。但是它们是有区别。 首先,yield方法是Thread对象提供方法,wait是Object对象提供方法可以看到线程一共有下面几种状态。 1. 新建(new):新创建了一个线程...
    yield方法和wait方法都会导致当前线程的停止。但是它们是有区别的。
     

    首先,yield方法是Thread对象提供的方法,wait是Object对象提供的方法。 

    其次,yield方法和sleep方法一样,在暂停的过程当中,并不会释放锁。锁其实是一段内存空间,并属于Object对象所有。因此其实获取锁或者释放锁都是需要通过Object对象来实现。

    再次,yield方法和wait方法虽然都可以让线程暂停,但是执行后线程的状态不一样。wait方法让线程进入到block状态,yield让线程进入到runnable状态。因此wait执行后的线程需要被唤醒。而yield方法执行后的线程不需要唤醒,可以直接再次获取CPU时间并执行。



    可以看到线程一共有下面的几种状态。
     
     
     
    1. 新建(new):新创建了一个线程对象。
    2. 可运行(runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
    3. 运行(running):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
    4. 阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种: 
    (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
    (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中
    (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
    5. 死亡(dead):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
     
    正常线程在start方法执行之后,进入到就绪状态,也就是可运行状态。这个时候线程就处于等待调度,获取CPU的使用权。
     
    yield方法的作用就是使得当前正在被CPU执行的线程让出CPU的使用权,回归到就绪状态。
    但是wait方法就比较不同了。wait方法会让CPU进入到一种阻塞状态,并且属于阻塞状态中的 等待阻塞状态。
    这个时候JVM会把该线程放入到等待队列中。直到被notify方法唤醒之后,会重新进入到就绪状态。下面是一个线程状态切换图:


    展开全文
  • 1.wait方法 This method should only be called by a thread that is the owner * of this object’s monitor. See the {@code notify} method for a * description of the ways in which a thread can become the ...
  • Object的wait()和Condition...Object的wait方法,是当前线程调用synchronized后获取到了对象的锁之后才可以使用 调用wait()让当前线程进入一个waitSet(可以理解为有一个休息室),这样其他等待同一个对象锁的线程
  • 主要介绍了java Object wait方法详细介绍相关资料,需要朋友可以参考下
  • Object wait方法

    千次阅读 2017-02-04 14:54:22
    但是拿到锁线程A在执行过程中,因为某些条件还不满足,暂时不想继续执行下去,想先等待一下(注意:是已经拿到锁线程A自己想主动等待),希望等到某个条件满足后,继续执行任务。在同步代码块里,线程A必须先...
  • Object的方法 void notify() 唤醒在此对象监视器上等待单个线程。 void notifyAll() 唤醒在此对象监视器上等待所有线程。 void wait() 在其他线程调用此对象 notify() 方法或 notifyAll() 方法前,...
  • 一、这里先来介绍下object的wait、notify和notify all方法 wait、notify和notifyAll方法是Object类的final native方法。所以这些方法不能被子类重写,Object类是所有类的超类,因此在程序中有以下三种形式调用...
  • java Object wait方法当线程B访问某个共享资源时,想获取资源锁对象,发现这个锁已经被线程A拿到了,这个时候,线程B只能被挂起,等待线程A释放锁。但是拿到锁线程A在执行过程中,因为某些条件还不满足,暂时...
  • 这三个方法必须配合synchronized 关键字使用, 直接使用会报java.lang.IllegalMonitorStateException
  • Object wait() 方法让当前线程进入等待状态。直到其他线程调用此对象notify() 方法或notifyAll() 方法。当前线程必须是此对象监视器所有者,否则还是会发生IllegalMonitorStateException异常。如果当前线程在...
  • 主要介绍了Java 中Object的wait() notify() notifyAll()方法使用的相关资料,需要的朋友可以参考下
  • 主要介绍了Objectwait及notify方法原理实例解析,文中通过示例代码介绍非常详细,对大家学习或者工作具有一定参考学习价值,需要朋友可以参考下
  • 1、wait可以让当前线程释放持有锁,并让线程处于wait状态 2、notify随机唤醒之前持有该锁对象处于wait状态线程,让其加入锁竞争(不会马上获得锁) 3、notifyAll唤醒所有之前持有该锁对象处于wait状态...
  • JDK中关于wait()方法和notify()方法说明英文版翻译,渣渣英语,如有不妥,望提出宝贵意见~ public final void wait() throws InterruptedException Causes the current thread to wait until another thread ...
  • waitObject的方法,对此对象调用wait 方法导致本线程放弃对象锁,进入等待此对象等待锁定池,只有针对此对象发出notify 方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。1、这...
  • Java Object.wait()方法

    千次阅读 2015-06-23 15:23:55
    java.lang.Object.wait()这个对象使当前线程等待,直到其他线程调用notify()方法或notifyAll()方法。换句话说,这种方法的行为完全一样,如果简单地执行调用wait(0). 当前线程必须拥有该对象监视器。线程释放...
  • 2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁),然后释放当前的锁,让出cpu; 3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多...
  • Object类九大方法之wait方法

    千次阅读 2019-08-13 13:22:46
    Object类九大方法之wait方法 wait、notify和notifyAll方法是Objectfinal native方法。所以这些方法不能被子类重写,Object类是所有类超类,因此在程序中有以下三种形式调用wait等方法。 wait();//方式1: ...
  • 各位小伙伴们大家好,在之前文章中,小编简单介绍了等待唤醒一些简单案例,这次小编要简单提一下Object类中wait带参方法和notify。一个是wait(long timeout),在其他线程调用此对象notify()方法或者...
  • Java Object wait()方法java.lang.Object.wait(long timeout, int nanos...此方法类似于wait方法的一个参数,但它允许更好地控制时间等待一个通知放弃之前量。实时量,以毫微秒计算,计算公式如下:1000000*time...
  • 在使用Object.wait()方法时抛出了异常,代码如下: /** * 所有处理线程等待对象 */ private Object waitObject; private boolean isRunning = false; public MessageHandleRunnable(Object waitObject,...
  • 相当于sleep( 5000 ) , 效果一样; 转载于:https://www.cnblogs.com/wmqiang/p/10666953.html
  • Object的wait、notify 和 notifyAll是Object提供的同步方法,也就是所有对象都生而带来的方法,估计搞java的没有不知道这几个方法的。那么他究竟是怎么使用的呢? 一.先说答案,再进行讲解 wait() 与 notify()/...
  • Java学习整理之Object的wait和notify方法

    千次阅读 2014-10-12 11:20:30
    Wait()和notify():如果条件不满足,则等待。当条件满足时,等待该条件线程将被唤醒。一般用在synchronized机制中。 例如:线程A  synchronized(obj) {  while(!condition) {  obj.wait();  

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,186
精华内容 2,474
关键字:

object的wait方法