精华内容
下载资源
问答
  • 很多朋友对java中的notyfy()和notifyAll()的本质区别不了解,今天小编抽空给大家整理一篇教程关于Java中的notyfy()和notifyAll()的本质区别,需要的朋友参考下吧
  • 本篇文章是对java的 wait(),notify(),notifyAll()进行了详细的分析介绍,需要的朋友参考下
  • python-notifyAll:一个可用于所有类型的通知(如SMS,邮件,推送)的库
  • 主要介绍了Java线程中的notifyAll唤醒操作,非常不错,具有参考借鉴价值,需要的朋友可以参考下
  • 主要介绍了Java wait和notifyAll实现简单的阻塞队列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 本文主要介绍Java notify和notifyAll的知识,这里整理详细的资料来说明notify 和NotifAll的区别,有需要的小伙伴可以参考下
  • 本篇文章是对java多线程 wait(),notify(),notifyAll()进行了详细的分析介绍,需要的朋友参考下
  • 主要介绍了Java 中Object的wait() notify() notifyAll()方法使用的相关资料,需要的朋友可以参考下
  • 既然notify会唤醒一个线程,并获取锁,notifyAll会唤醒所有线程并根据算法选取其中一个线程获取锁,那最终结果不都是只有一个线程获取锁吗?那JDK为什么还需要做出来这两个方法呢?这两种同步方法本质上会有什么区别...

    1.代码示例

    1.1 生产者

    package com.example.hxk.thread.demo;
    
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author Smith 2019/3/21
     */
    public class Producer implements Runnable{
    
        List<Integer> cache;
    
        public void put() throws InterruptedException {
            synchronized (cache) {
                while (cache.size() == 1) {
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
    
                TimeUnit.SECONDS.sleep(1);
    
                cache.add(1);
                System.out.println(Thread.currentThread().getName() + "生产者生产了一条。");
                cache.notify();
            }
        }
    
        public Producer(List<Integer> cache) {
            this.cache = cache;
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    put();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    1.2 消费者

    package com.example.hxk.thread.demo;
    
    import java.util.List;
    
    /**
     * @author Smith 2019/3/21
     */
    public class Customer implements Runnable {
    
        List<Integer> cache;
    
        public Customer(List<Integer> cache) {
            this.cache = cache;
        }
    
        private void custom() {
            synchronized (cache) {
                while (cache.size() == 0) {
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                cache.remove(0);
                System.out.println(Thread.currentThread().getName() + "消费者消费了一条。");
                cache.notify();
            }
        }
        @Override
        public void run() {
            while (true) {
                custom();
            }
        }
    }
    

    1.3 测试代码

    1.3.1 一个生产者一个消费者

    package com.example.hxk.thread.demo;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Smith 2019/3/21
     */
    public class Test {
    
        public static void main(String[] args) {
    
            List<Integer> cache = new ArrayList<>();
            new Thread(new Producer(cache), "P1").start();
            new Thread(new Customer(cache), "C1").start();
        }
    }
    

    运行结果:

     

     

     

    结论:

    使用notify且一个生产者一个消费者的情况下,生产和消费有条不紊的运行,没有任何问题。

    1.3.2 一个生产者两个消费者

    package com.example.hxk.thread.demo;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Smith 2019/3/21
     */
    public class Test {
    
        public static void main(String[] args) {
    
            List<Integer> cache = new ArrayList<>();
    
            new Thread(new Producer(cache), "P1").start();
            new Thread(new Customer(cache), "C1").start();
            new Thread(new Customer(cache), "C2").start();
        }
    }
    
    复制代码

    运行结果:

     

     

     

    结论:

    使用notify且一个生产者两个消费者的情况下,生产了两次后,程序死锁了。

    1.3.3 将Producer和Customer中的notify()换成notifyAll()

    代码就不粘了。

    运行结果

     

     

     

    程序又恢复了正常。

    2.notify()和notifyAll的区别

    每个同步对象都有自己的锁池和等待池。

    2.1 锁池和等待池

    • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
    • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.

    2.2 notify()和notifyAll()的区别

    • 线程调用了wait()方法,便会释放锁,并进入等待池中,不会参与锁的竞争
    • 调用notify()后,等待池中的某个线程(只会有一个)会进入该对象的锁池中参与锁的竞争,若竞争成功,获得锁,竞争失败,继续留在锁池中等待下一次锁的竞争。
    • 调用notifyAll()后,等待池中的所有线程都会进入该对象的锁池中参与锁的竞争。

    3.例子解析

    知道了2的知识后,上面的三个例子也就好解释了。

    1.3.1:因为只有一个生产者和消费者,所以等待池中始终只有一个线程,要么就是生产者唤醒消费者,要么消费者唤醒生产者,所以程序可以成功跑起来;

    1.3.2:举个可能的例子

    1. 现在有三个线程,生产者P1, 消费者C1和C2.开始运行的时候,三个都在锁池中等待竞争,假设C1抢到锁了,C1执行时由于没有资源可以消费 调用wait()方法,释放锁并进入等待池。
    2. C2抢到了锁,开始消费,同理,C2也进入了等待池。现在锁池里面只剩下了P1.
    3. P1获得了锁,开始生产,生产完成后,P1开始调用notify()方法唤醒等待池中的C1或者C2,然后P1调用wait()方法释放锁,并进入了等待池。
    4. 假设唤醒的是C1,C1进入锁池并获得锁,消费后notify()方法唤醒了C2,C2进入锁池,C1进入等待池,现在锁池中只有C1。
    5. C1获得了锁,发现没有任何资源可以消费,wait()后释放了锁,进入了等待池,现在三个线程全都在等待池,锁池中没有任何线程。导致死锁!

    1.3.3: notifyAll()后,不存在只唤醒同类线程的情况,故也就不会出现1.3.2死锁的情况。

    引用

     

     

    区别
    notify:只会唤醒等待该锁的其中一个线程。
    notifyAll:唤醒等待该锁的所有线程。
    既然notify会唤醒一个线程,并获取锁,notifyAll会唤醒所有线程并根据算法选取其中一个线程获取锁,那最终结果不都是只有一个线程获取锁吗?那JDK为什么还需要做出来这两个方法呢?这两种同步方法本质上会有什么区别?

    这还要从对象内部锁的调度说起。

    对象内部锁
    其实,每个对象都拥有两个池,分别为锁池(EntrySet)和(WaitSet)等待池。

    锁池:假如已经有线程A获取到了锁,这时候又有线程B需要获取这把锁(比如需要调用synchronized修饰的方法或者需要执行synchronized修饰的代码块),由于该锁已经被占用,所以线程B只能等待这把锁,这时候线程B将会进入这把锁的锁池。
    等待池:假设线程A获取到锁之后,由于一些条件的不满足(例如生产者消费者模式中生产者获取到锁,然后判断队列为满),此时需要调用对象锁的wait方法,那么线程A将放弃这把锁,并进入这把锁的等待池。
    如果有其他线程调用了锁的notify方法,则会根据一定的算法从等待池中选取一个线程,将此线程放入锁池。
    如果有其他线程调用了锁的notifyAll方法,则会将等待池中所有线程全部放入锁池,并争抢锁。

    锁池与等待池的区别:等待池中的线程不能获取锁,而是需要被唤醒进入锁池,才有获取到锁的机会。

    问题复现
    那么使用notify和notifyAll到底会有什么区别呢?
    请看下面一组生产者消费者的例子。
    有两个生产者t1和t2,两个消费者t3和t4,以及一个长度为1的队列。

    1. 初始状态,这四个线程全部进入锁池,等待抢占锁。
    2. t3获取到锁,但是队列为空,故t3进入等待池。
    3. t4获取到锁,但是队列为空,故t4进入等待池。
    4. t1获取到锁,生产,队列满,调用notify,唤醒一个线程。由于此时t3和t4都在等待池中,所以会有一个线程从等待池进入锁池,假设此处t3进入锁池。
    5. 此时,锁池有t2和t3两个线程,假设t2获取到了锁,但是队列满,故t2进入等待池,放弃锁。
    6. 此时,t3获取到锁,消费,notify,由于此时等待池有两个线程t2和t4,假如唤醒的是t2,没问题开始生产,但是若唤醒的是t4,则因队列为空,继续wait。
    7. 此时若t1和t3已经执行结束,t1不在生产,t3不再消费,则t2和t4会一直留在等待池,行程死锁。

    如果此处使用notifyAll,则会把等待池中所有线程唤醒,不会形成所有线程都位于等待池,无法唤醒的情况,也就不会形成死锁,当然了,使用notifyAll方法会更加低效一些。

    如果此处是一个生产者一个消费者的情况,使用notify没有任何问题,且效率更高。

    展开全文
  • Java多线程中notifyAll()方法使用教程

    千次阅读 2020-10-27 22:33:11
    1.`notifyAll()`中`All`的含义是所有的线程,而不是所有的锁,只能唤醒等待(调用wait()方法等待)同一个锁的所有线程,这一点一定要注意。 2.`notifyAll()`必须在当前线程拥有监视器锁的情况下执行,否则将抛出异常...

    简介

    本文将承接《Java多线程wait()和notify()系列方法使用教程》,结合代码实例,补充讲解下notifyAll()方法的作用以及使用时需要注意的地方。

    一.notifyAll()方法在JDK中的定义

    notifyAll()唤醒正在等待此对象监视器锁的所有线程。
    

    注意:

    1.notifyAll()All的含义是所有的线程,而不是所有的锁,只能唤醒等待(调用wait()方法等待)同一个锁的所有线程,这一点一定要注意。

    2.notifyAll()必须在当前线程拥有监视器锁的情况下执行,否则将抛出异常IllegalMonitorStateException。意思是说必须在同步代码块中,调用此方法,否则可能出现在没有得到锁的情况下,执行了此方法,导致程序异常。wait()notify()系列方法这样设计的目的是防止死锁或永久等待发生。

    3.notifyAll()方法任何对象都可以调用,并且无法重写此方法,因为被final修饰。

    4.notifyAll()只能释放一把锁。单独列出此条,这很重要。

    5.notifyAll()执行后,只有一个线程能得到锁,其他没有得到锁的线程会继续保持在等待状态。

    二.讲解一个标准的notifyAll()方法使用代码

    代码逻辑描述:

    1. 线程thread0thread1共同竞争object对象锁,并调用各自的wait()方法等待。
    2. 线程thread2负责通过notifyAll()唤醒线程。
    public class ThreadWaitAndNotifyAll implements Runnable {
    
    	// 模拟多个线程的共享变量
    	private static Object object = new Object();
    
    	@Override
    	public void run() {
    		synchronized (object) {
    			System.out.println("线程" + Thread.currentThread().getName() + "获得锁,进入等待状态");
    			try {
    				object.wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println("线程" + Thread.currentThread().getName() + "执行最后部分结束");
    		}
    	}
    
    	public static void main(String[] args) {
    		ThreadWaitAndNotifyAll runnable = new ThreadWaitAndNotifyAll();
    		Thread thread0 = new Thread(runnable);
    		Thread thread1 = new Thread(runnable);
    		Thread thread2 = new Thread(new Runnable() {
    			@Override
    			public void run() {
    				synchronized (object) {
    					System.out.println("线程" + Thread.currentThread().getName() + "获得锁,开始通知唤醒所有线程");
    					// 唤醒其他线程
    					object.notifyAll();
    					// 调用完notifyAll()方法后,同步代码块中的其他代码,必须执行完后才能将对象锁释放,而不是调用了notifyAll()方法后立即释放。
    					System.out.println("线程" + Thread.currentThread().getName() + "执行结束");
    				}
    			}
    		});
    		// 每次运行,线程0和线程1的顺序可能会不同,执行顺序由CPU决定
    		thread0.start();
    		thread1.start();
    		try {
    			// 加一个延时,让线程2一定在线程0和1之后执行,否则线程2中的notifyAll方法将无效
    			Thread.sleep(100);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		thread2.start();
    	}
    }
    

    执行结果:

    线程Thread-0获得锁,进入等待状态
    线程Thread-1获得锁,进入等待状态
    线程Thread-2获得锁,开始通知唤醒所有线程
    线程Thread-2执行结束
    线程Thread-1执行最后部分结束
    线程Thread-0执行最后部分结束
    

    执行结果分析:

    1. thread0thread1启动后,会各自执行自己的代码,在执行到wait()方法后,进入等待状态。
    2. thread2在启动之前,特意增加了sleep延时,为了保证在前两个线程之后运行,保证了执行顺序。
    3. thread2执行了notifyAll()方法并执行完synchronized代码块其他代码后,CPU才会将对象object锁随机分配给thread0thread1(二者只能选其一)。
    4. 假如thread1得到锁,它会继续执行其wait()方法之后代码,知道同步代码块内容全部执行完毕后,cpu才会将对象object锁分配给正在等待的线程thread0
    5. thread0得到所以后,也会从wait()方法处继续执行,直到同步代码块结束才会释放对object对象的锁。

    注意: 调用完notifyAll()方法后,同步代码块中的其他代码,必须执行完后才能将对象锁释放,而不是调用了notifyAll()方法后立即释放。

    三.关于notifyAll()非常重要的隐藏知识点

    在java中,Thread类线程执行完run()方法后,一定会自动执行notifyAll()方法

    这个细节隐藏在Java的Native方法中,所以一般不会被人发现。我们观察C/C++源码,如下:

    oid JavaThread::exit(booldestory_vm, ExitTypeexit_type);
    static void ensure_join(JavaThread*thread) {
    	Handle threadObj(thread, thread -> threadObj());
    	ObjectLocker lock(threadObj, thread);
    	thread -> clear_pending_exception();
    	java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
    	java_lang_Thread::set_thread(threadObj(), NULL);
         //下行执行了notifyAll()操作
    	lock.notify_all(thread);
    	thread -> clear_pending_exception();
    }
    

    其中ensure_join就是执行join()方法,等方法执行结束时,此行代码lock.notify_all(thread);意思是通过notifyAll()唤醒了所有等待线程。所以在使用线程的时候,要特别注意。

    总结

    本文讲解了notifyAll()方法的用法后,notify系列方法就都已经讲解完成了,接下来的文章中,会在实现几个重要的、具体的场景,来使用这些方法做一些事情,看看这些方法经常被用在什么地方。喜欢本文请继续关注后续文章以及收藏和点赞。

    展开全文
  • wait与notify,notifyAll 他们都是Object类的方法,一般用在synchronized代码块中,用于当线程某个条件不满足时,通过wait()挂起线程,将线程加入到阻塞队列中。而当条件满足时,通过notify唤醒阻塞的线程,被唤醒的...

    wait与notify,notifyAll

    他们都是Object类的方法,一般用在synchronized代码块中,用于当线程某个条件不满足时,通过wait()挂起线程,将线程加入到阻塞队列中。而当条件满足时,通过notify唤醒阻塞的线程,被唤醒的线程将会从wait()阻塞的位置继续执行。
    这是官方推荐的使用格式是:

        synchronized (obj) {
           while (条件不成立)
                obj.wait();
          ... // Perform action appropriate to condition
        }
    

    通过以上的方式,当线程被唤醒时,会继续判断条件是否成立。如果成立,则继续执行。
    notify与notifyAll的区别是:notify一次只唤醒一个线程,notifyAll会唤醒阻塞队列中的所有线程。

    为什么建议尽量使用notifyAll,而不用notify

    看个例子:

    package juc;
    
    /**
     * <pre>类名: ObjectWaitNotifyTest</pre>
     * <pre>描述: 等待-通知机制</pre>
     * <pre>版权: web_chen@163.com</pre>
     * <pre>日期: 2020/11/5 11:06</pre>
     * <pre>作者: chenwb</pre>
     */
    public class ObjectWaitNotifyTest {
    	/**
    	 * 共享变量
    	 */
    	private int shareVal = 0;
    
    	public synchronized void addShareVal1() throws InterruptedException {
    		while (shareVal % 3 != 0) {
    			wait();
    		}
    		shareVal++;
    		System.out.println(shareVal);
    		notify();
    	}
    
    	public synchronized void addShareVal2() throws InterruptedException {
    		while (shareVal % 3 != 1) {
    			wait();
    		}
    		shareVal++;
    		System.out.println(shareVal);
    		notify();
    	}
    
    	public synchronized void addShareVal3() throws InterruptedException {
    		while (shareVal % 3 != 2) {
    			wait();
    		}
    		shareVal++;
    		System.out.println(shareVal);
    		notify();
    	}
    
    	public static void main(String[] args) throws InterruptedException {
    		ObjectWaitNotifyTest objectWaitNotify = new ObjectWaitNotifyTest();
    		Thread t1 = new Thread(() -> {
    			try {
    				for (; ; ) objectWaitNotify.addShareVal1();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		},"t1");
    
    		Thread t2 = new Thread(() -> {
    			try {
    				for (;;) objectWaitNotify.addShareVal2();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		},"t2");
    
    		Thread t3 = new Thread(() -> {
    			try {
    				for (; ; ) objectWaitNotify.addShareVal3();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		},"t3");
    
    		t1.start();
    		t2.start();
    		t3.start();
    		t1.join();
    		t2.join();
    		t3.join();
    	}
    }
    

    上例中我定义了三个线程,一个线程只能对3的倍数做+1操作,一个线程只能对3的倍数多1的数做+1操作,一个线程只能对3的倍数多2的数做+1操作,否则线程就会被挂起,等待条件符合。
    这里的wait()与notify()等价于this.wait()与this.notify(),锁的是当前对象。
    当执行完+1操作之后,就唤醒下一个线程,看似没什么问题。
    执行代码结果:

    1
    2
    

    执行不下去了。。。
    通过jstack -l pid 查看线程状态:

    "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000054013000 nid=0x349c in Object.wait() [0x000000005552f000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x00000000f56898b0> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
            - locked <0x00000000f56898b0> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
            at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
    
       Locked ownable synchronizers:
            - None
    
    "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000053fcb000 nid=0x23ac in Object.wait() [0x000000005542f000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x00000000f5689ae0> (a java.lang.ref.Reference$Lock)
            at java.lang.Object.wait(Object.java:502)
            at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
            - locked <0x00000000f5689ae0> (a java.lang.ref.Reference$Lock)
            at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
    
       Locked ownable synchronizers:
            - None
    

    可以看到线程是阻塞的无法继续执行。

    原因分析

    正因为notify只随机唤醒一个阻塞线程,如果t1,t2线程都阻塞了,线程t3执行完+1之后执行notify,而唤醒的线程是t2,而t2不满足执行条件,线程t1的条件才是符合的。此时线程就都阻塞了。
    而使用notifyAll,会唤醒所有阻塞线程,只要有一个线程符合条件就可以继续执行,就不会出现这种情况了。

    将notify改为notifyAll

    package juc;
    
    /**
     * <pre>类名: ObjectWaitNotifyTest</pre>
     * <pre>描述: 等待-通知机制</pre>
     * <pre>版权: web_chen@163.com</pre>
     * <pre>日期: 2020/11/5 11:06</pre>
     * <pre>作者: chenwb</pre>
     */
    public class ObjectWaitNotifyTest {
    
    	/**
    	 * 共享变量
    	 */
    	private int shareVal = 0;
    
    	public synchronized void addShareVal1() throws InterruptedException {
    		while (shareVal % 3 != 0) {
    			wait();
    		}
    		shareVal++;
    		System.out.println(shareVal);
    		notifyAll();
    	}
    
    	public synchronized void addShareVal2() throws InterruptedException {
    		while (shareVal % 3 != 1) {
    			wait();
    		}
    		shareVal++;
    		System.out.println(shareVal);
    		notifyAll();
    	}
    
    	public synchronized void addShareVal3() throws InterruptedException {
    		while (shareVal % 3 != 2) {
    			wait();
    		}
    		shareVal++;
    		System.out.println(shareVal);
    		notifyAll();
    	}
    
    	public static void main(String[] args) throws InterruptedException {
    		ObjectWaitNotifyTest objectWaitNotify = new ObjectWaitNotifyTest();
    		Thread t1 = new Thread(() -> {
    			try {
    				for (; ; ) objectWaitNotify.addShareVal1();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		},"t1");
    
    		Thread t2 = new Thread(() -> {
    			try {
    				for (;;) objectWaitNotify.addShareVal2();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		},"t2");
    
    		Thread t3 = new Thread(() -> {
    			try {
    				for (; ; ) objectWaitNotify.addShareVal3();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		},"t3");
    
    		t1.start();
    		t2.start();
    		t3.start();
    		t1.join();
    		t2.join();
    		t3.join();
    	}
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    ...
    ...
    

    执行成功!

    小结

    1、notifyAll相比notify,是更加耗性能的,但是更加稳妥。相比安全,可靠来说,耗费一点性能是可以接受的。
    2、除非你完全确定,否则建议你使用notifyAll替代notify。
    使用notify的条件:

    • 所有等待线程拥有相同的等待条件;
    • 所有等待线程被唤醒后,执行相同的操作;
    • 只需要唤醒一个线程。
    展开全文
  • Java中notify和notifyAll的区别 - 何时以及如何使用

    万次阅读 多人点赞 2018-07-13 18:24:34
    用Java通知vs notifyAllnotify和notifyAll方法之间有什么区别是棘手的Java问题之一,这很容易回答但是一旦访问者提出后续问题,你要么感到困惑,要么无法提供明确的答案? notify和notifyAll之间的主要区别在于...

    Java  notify   vs notifyAll

     

    • notify和notifyAll方法之间有什么区别是棘手的Java问题之一!

    • Condition 是个什么玩意?

    提几个问题,从问题中去了解去学习:

    1. 他们之间有啥区别?
    2. 如果我使用notify(),将通知哪个线程?
    3. 我怎么知道有多少线程在等待,所以我可以使用notifyAll()?
    4. 如何调用notify()?
    5. 什么是这些线程等待被通知等?

    我给点建议:建议使用jdk8里的lock包

    1. java.util.concurrent.locks下的Condition 他可以支持唤醒指定的线程。
    2.  他只是一个接口 具体实现类是在
      AbstractQueuedSynchronizer 也就是AQS框架里的 你可以自己继承他 或者使用 ReentrantLock里的newConditon()方法来获取

    解决下问题:

    • Java中notify和notifyAll的区别
    1. Java提供了两个方法notify和notifyAll来唤醒在某些条件下等待的线程,你可以使用它们中的任何一个,但是Java中的notify和notifyAll之间存在细微差别,这使得它成为Java中流行的多线程面试问题之一。当你调用notify时,只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒,这取决于线程调度器。虽然如果你调用notifyAll方法,那么等待该锁的所有线程都会被唤醒,但是在执行剩余的代码之前,所有被唤醒的线程都将争夺锁定,这就是为什么在循环上调用wait,因为如果多个线程被唤醒,那么线程是将获得锁定将首先执行,它可能会重置等待条件,这将迫使后续线程等待。因此,notify和notifyAll之间的关键区别在于notify()只会唤醒一个线程,而notifyAll方法将唤醒所有线程。
    •     何时在Java中使用notify和notifyAll
    1. 如果所有线程都在等待相同的条件,并且一次只有一个线程可以从条件变为true,则可以使用notify over notifyAll。
    2. 在这种情况下,notify是优于notifyAll 因为唤醒所有这些因为我们知道只有一个线程会受益而所有其他线程将再次等待,所以调用notifyAll方法只是浪费CPU。
    3. 虽然这看起来很合理,但仍有一个警告,即无意中的接收者吞下了关键通知。通过使用notifyAll,我们确保所有收件人都会收到通知

     

    • Java中通知和notifyAll方法的示例(后序demo示例代码 )
    1. 我已经汇总了一个示例来说明当我们在Java中调用notifyAll方法时如何通知所有线程,并且当我们在Java中调用notify方法时,只有一个Thread会被唤醒。
    2. 在这个例子中,如果boolean变量go为false,则三个线程将等待,记住boolean go是一个volatile变量,以便所有线程都能看到它的更新值。
    3. 最初三个线程WT1,WT2,WT3将等待,因为变量go为假,而一个线程NT1将变为真,并通过调用notifyAll方法通知所有线程,或通过调用notify()方法通知一个线程。在notify()调用的情况下,无法保证哪个线程会被唤醒,您可以通过多次运行此Java程序来查看它。
    4. 在notifyAll的情况下,所有线程都将被唤醒,但是它们将竞争监视器或锁定,并且将首先获得锁定的线程将完成其执行并且重置为false将迫使其他两个线程仍在等待。在该程序结束时,将有两个线程在等待,两个线程包括通知线程完成。程序不会终止,因为其他两个线程仍在等待,并且它们不是守护程序线程。
    5. 实例代码如下:以下是如何在Java中使用notify和notifyAll方法的完整代码示例。在解释了何时使用notify vs notifyAll方法,这个例子将阐明在Java中调用notify和notifyAll方法的效果。go!

     

    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    /**
     * Java程序演示如何在Java和Java中使用notify和notifyAll方法
      *如何通知和notifyAll方法通知线程,哪个线程被唤醒等。
     */
    public class NotificationTest {
    
        private volatile boolean go = false;
    
        public static void main(String args[]) throws InterruptedException {
            final NotificationTest test = new NotificationTest();
          
            Runnable waitTask = new Runnable(){
          
                @Override
                public void run(){
                    try {
                        test.shouldGo();
                    } catch (InterruptedException ex) {
                        Logger.getLogger(NotificationTest.class.getName()).
                               log(Level.SEVERE, null, ex);
                    }
                    System.out.println(Thread.currentThread() + " finished Execution");
                }
            };
          
            Runnable notifyTask = new Runnable(){
          
                @Override
                public void run(){
                    test.go();
                    System.out.println(Thread.currentThread() + " finished Execution");
                }
            };
          
            Thread t1 = new Thread(waitTask, "WT1"); //will wait
            Thread t2 = new Thread(waitTask, "WT2"); //will wait
            Thread t3 = new Thread(waitTask, "WT3"); //will wait
            Thread t4 = new Thread(notifyTask,"NT1"); //will notify
          
            //starting all waiting thread
            t1.start();
            t2.start();
            t3.start();
          
            //pause to ensure all waiting thread started successfully
            Thread.sleep(200);
          
            //starting notifying thread
            t4.start();
          
        }
        /*
         * wait and notify can only be called from synchronized method or bock
         */
        private synchronized void shouldGo() throws InterruptedException {
            while(go != true){
                System.out.println(Thread.currentThread()
                             + " is going to wait on this object");
                wait(); //release lock and reacquires on wakeup
                System.out.println(Thread.currentThread() + " is woken up");
            }
            go = false; //resetting condition
        }
      
        /*
         * both shouldGo() and go() are locked on current object referenced by "this" keyword
         */
        private synchronized void go() {
            while (go == false){
                System.out.println(Thread.currentThread()
                + " is going to notify all or one thread waiting on this object");
    
                go = true; //making condition true for waiting thread
                //notify(); // only one out of three waiting thread WT1, WT2,WT3 will woke up
                notifyAll(); // all waiting thread  WT1, WT2,WT3 will woke up
            }
          
        }
      
    }

    使用notify时的输出
    Thread[WT1,5,main] is going to wait on this object
    Thread[WT3,5,main] is going to wait on this object
    Thread[WT2,5,main] is going to wait on this object
    Thread[NT1,5,main] is going to notify all or one thread waiting on this object
    Thread[WT1,5,main] is woken up
    Thread[NT1,5,main] finished Execution
    Thread[WT1,5,main] finished Execution

    使用notifyAll时的输出
    Thread[WT1,5,main] is going to wait on this object
    Thread[WT3,5,main] is going to wait on this object
    Thread[WT2,5,main] is going to wait on this object
    Thread[NT1,5,main] is going to notify all or one thread waiting on this object
    Thread[WT2,5,main] is woken up
    Thread[NT1,5,main] finished Execution
    Thread[WT3,5,main] is woken up
    Thread[WT3,5,main] is going to wait on this object
    Thread[WT2,5,main] finished Execution
    Thread[WT1,5,main] is woken up
    Thread[WT1,5,main] is going to wait on this object

    强烈建议运行这个Java程序并理解它产生的输出并尝试理解它。除了死锁,竞争条件和线程安全之外,线程间通信是Java中并发编程的基础之一。

    总结:

    1)如果我使用notify(),将通知哪个线程?
    无法保证,ThreadScheduler将从等待该监视器上的线程的池中选择一个随机线程。保证只有一个线程会被通知:(随机性)


    2) 我怎么知道有多少线程在等待,所以我可以使用notifyAll()?
    它取决于程序逻辑,在编码时需要考虑一段代码是否可以由多个线程运行。理解线程间通信的一个很好的例子是在Java中实现生产者 - 消费者模式。


    3) 如何调用notify()?
    Wait()和notify()方法只能从synchronized方法或块中调用,需要在其他线程正在等待的对象上调用notify方法。


    4) 什么是这些线程等待被通知等?
    线程等待某些条件,例如在生产者 - 消费者问题中,如果共享队列已满,则生产者线程等待,如果共享队列为空,则生成者线程等待。由于多个线程正在使用共享资源,因此它们使用wait和notify方法相互通信。


    这就是Java中的notify和notifyAll方法之间的区别以及何时在Java中使用notify vs notifyAll。现在,应该能够理解并使用notify和notifyAll方法在Java程序中进行线程间通信。

    补充下建议里的:lock包下的condition (demo里 是典型的生产者消费者模式》》》 使用的是condition来实现)

       final Lock lock = new ReentrantLock();
       //定义2组condition 对应生产者消费者
       final Condition notFull  = lock.newCondition(); 
      
       final Condition notEmpty = lock.newCondition(); 
    
       final Object[] items = new Object[100];
       int putptr, takeptr, count;
        //在put的时候 当数组已经满了的情况下 我让线程等待 不在容纳数据 当消费者已经消费了 触发了、、
    
    //notfull.signal() 这时候通知生产者 我这变已经消费了 你那边可以试试了哈。 
       public void put(Object x) throws InterruptedException {
         lock.lock();
         try {
           while (count == items.length)
             notFull.await();
           items[putptr] = x;
           if (++putptr == items.length) putptr = 0;
           ++count;
           notEmpty.signal();
         } finally {
           lock.unlock();
         }
       }
    //同上 相反的理解就是了。。。
       public Object take() throws InterruptedException {
         lock.lock();
         try {
           while (count == 0)
             notEmpty.await();
           Object x = items[takeptr];
           if (++takeptr == items.length) takeptr = 0;
           --count;
           notFull.signal();
           return x;
         } finally {
           lock.unlock();
         }
       }

     Condition因素出Object监视器方法(waitnotify 和notifyAll)为不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的相结合的效果Lock的实施方式。如果Lock替换synchronized方法和语句Condition的使用,则替换Object监视方法的使用。

    条件(也称为条件队列或 条件变量)为一个线程提供暂停执行(“等待”)的手段,直到另一个线程通知某个状态条件现在可能为真。由于对此共享状态信息的访问发生在不同的线程中,因此必须对其进行保护,因此某种形式的锁定与该条件相关联。等待条件提供的关键属性是它以原子方式释放关联的锁并挂起当前线程,就像它一样Object.wait

    一个Condition实例本质上绑定到一个锁。要获取Condition特定Lock 实例的实例,请使用其newCondition()方法。

    举个例子,假设我们有一个支持puttake方法的有界缓冲区 。如果take在空缓冲区上尝试a ,则线程将阻塞直到某个项可用; 如果put在完整缓冲区上尝试a,则线程将阻塞,直到空间可用。我们希望 在单独的等待集中保持等待put线程和take线程,以便我们可以使用仅在缓冲区中的项或空间可用时通知单个线程的优化。

    也就是说 可以创建多个condition 每组condition 对应你的具体的线程操作 当你

    notFull.signalAll();的时候 你唤醒的也只是你这组condition里的等待线程 对于不在这组里的notEmpty是没有任何影响的 

    现在 你是不是可以随心所欲的唤醒你想唤醒的线程了? 都看到这了 还不给个赞吗

     

     

     

     

     

     

    展开全文
  • notify和notifyall的区别

    千次阅读 2020-03-26 16:48:38
    notifyAll()也能达到同样的效果 notify和notifyAll的区别 首先讲这俩区别之前先来了解两个概念。锁池EntryList和等待池WaitSet。而这两个池和Object基类的notify 锁池 假设线程A已经拥有了某个对象(不是类)的锁,而...
  • 线程最终是在锁池还是在等待池 线程之间通信有多种方式比如:Lock锁的Condition、CountDownLantch、CyclicBarrier、Semaphore和本文讲到的Object类中的wait()、notify()、notifyAll()方法。wait()、notify()、...
  • notifyAll是Object的方法 ---public final native void notifyAll();但不是静态方法 这个在代码1中,为什么没有实例对象就可以直接调用该方法 而在代码2中为什么需要 restaurant.waitPerson.notifyAll() ;如果...
  • notify() 和 notifyAll() 有什么区别?

    万次阅读 多人点赞 2019-05-15 16:22:16
    notify() 和 notifyAll() 有什么区别? 先解释两个概念。 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池,等待池中的线程不会去竞争该对象的锁。 锁池...
  • 锁池和等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些...notify和notifyAll的区别   如果线程调用了对象的 wait
  • 阻塞阶段 执行wait 要获得这个对象的monitor 锁 ,调用...另一个线程调用这个对象的notifyAll()方法; 过了wait(long timeout)规定的超时时间,如果传入0就是永久等待; 线程自身调用了interrupt() 唤醒阶段 ...
  • 线程间也可以通过 wati()、notify()和notifyAll()等方法进行通讯。 每一个类的对象的实例都有一个等待集合,当在该实例上调用wait()方法后,线程都会进入到该实例的等待集合中。 wait()、notify() 和 notifyAll()...
  • Java notify和notifyAll源码分析与性能对比 一、源码剖析 首先,看看在synchronizer.cpp中notify和notifyall的实现: void ObjectSynchronizer::notify(Handle obj, TRAPS) { if (UseBiasedLocking) { ...
  • 很多人在回答第二个问题的时候会想当然的说notify()是唤醒一个线程,notifyAll()是唤醒全部线程,但是唤醒然后呢,不管是notify()还是notifyAll(),最终拿到锁的只会有一个线程,那它们到底有什么区别呢?...
  • java中的notify和notifyAll有什么区别?

    万次阅读 多人点赞 2018-03-01 11:32:27
    综上,所谓唤醒线程,另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。...
  • 详解java中wait/notifiy/notifyAll1. 基础使用方法2. 状态依赖性的管理2.1 什么是状态依赖性2.1.1 定义2.1.2 单线程和多线程的状态依赖性2.2 内置条件队列2.3 前提条件失败传递给调用者2.4 使用轮询与休眠解决状态...
  • notifyAll() 不同于在共享变量上调用notify()函数会唤醒被阻塞到该共享变量上的一个线程,notifyAll()方法则会唤醒所有在该共享变量上由于调用wait系列方法而被挂起的线程。 package thread.notify; public class ...
  • 线程作为程序内部的多个执行流,相互之间是可以通讯的。... 线程间也可以通过 wati()、notify()和notifyAll()等方法进行通讯。 每一个类的对象的实例都有一个等待集合,当在该实例上调用wait()方法后,线程都...
  • 聊聊并发:(三)wait、sleep、notify、notifyAll分析

    万次阅读 多人点赞 2018-08-05 15:22:21
    Java中提供了很多种方法对线程的状态进行控制以及线程之间的通信,包括wait、notify、notifyAll、sleep,下面我们就来看一下它们之间有什么区别,以及如何使用这些方法进行线程状态的控制与通信。 线程之间的通信...
  • wait、notify、notifyAll 是 Object 中的方法,可以作用于任何对象,用于控制线程的状态,通常配合 synchronized 代码块使用 Sleep(): sleep 的作用是使当前线程睡眠指定的时间,放弃对CPU的占...
  • 1、wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写。 2、wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用,即,一般在synchronized 同步代码块里使用 wait()、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 100,982
精华内容 40,392
关键字:

notifyall