notify / notifyall是否释放正在进行的锁定

我有点担心等待并通知/ notifyAll。

我知道每个java对象都有一个锁。 我知道等待将释放其他线程的锁。 通知/ notifyall怎么样? notify / notifyAll会释放它为其他线程持有的锁吗?

否 – notify / notifyAll不释放像wait这样的锁。 唤醒的线程无法运行,直到调用notify的代码释放其锁定。

这就是Javadoc所说的:

该线程释放此监视器的所有权并等待,直到另一个线程通过调用notify方法或notifyAll方法通知等待此对象监视器的线程唤醒。 然后线程等待,直到它可以重新获得监视器的所有权并继续执行。

  • wait()告诉调用线程放弃监视器并进入hibernate状态,直到某个其他线程进入同一个监视器并调用notify()。

  • notify()唤醒在同一个对象上调用wait()的线程。

  • notifyAll()唤醒在同一对象上调用wait()的所有线程。 优先级最高的线程将首先运行。

不同意那些说notifyAll()释放对象的锁定的人,在该对象上等待和通知线程正在同步。

一个例子:

Consumer类包含一个块:

 synchronized(sharedObject){ if(sharedObject.isReadyToConsume() == false){ sharedObject.wait(); }else { sharedObject.doTheThing(); System.out.println("consumer consuming..."); } 

}

场景: Consumer类获取对sharedObject对象的锁定,独占进入(它在同步块内)并且看到sharedObject还没有准备就绪(没有消耗:))并且它在sharedObject上调用wait()方法。 这样它就会释放锁(在那里停止执行!)并等待另一个线程(生产者可能)调用sharedObject.notify();时被通知继续sharedObject.notify();sharedObject.notifyAll(); 。 当它收到通知时,它会从wait()行继续

它是sharedObject ,用于跟踪要求通知的线程。 当一些Thread调用sharedObject.notifyAll()方法时, sharedObject将通知等待的线程被唤醒…现在,棘手的部分是一个线程在到达其synchronized(sharedObject)的末尾时自然释放对象的锁定{}阻止 。 问题是如果我在该块中调用notifyAll()会发生什么notifyAll()唤醒等待的线程,但锁仍然由刚刚调用notifyAll()的线程拥有

查看Producer片段:

 synchronized(sharedObject){ //We are exlusively working with sharedObject and noone can enter it [... changing the object ...] sharedObject.notifyAll(); //notifying the waiting threads to wake up Thread.sleep(1000); //Telling the current thread to go to sleep. It's holding the LOCK System.out.println("awake..."); 

}

如果notifyAll()将释放锁定,那么在Consumer类已经开始使用sharedObject之后,“awake …”将被打印出来。 情况并非如此……输出显示Consumer在Producer退出同步块后消耗了sharedObject …

  • wait() – 释放锁定并在收到通知后继续执行下一行
  • notify(),notifyAll() – 不释放锁。 它们只是让等待的线程再次可以运行(而不是空闲)。 当当前线程到达其同步块的末尾并且Thread scheduleder告诉他们锁已被释放时,他们将有权进入。 对锁的争夺再次开始

让我们说一堆读者想要读取某些资源的更新值,这将由Writer更新。 那么Reader如何知道Writer已经更新了资源字段。

因此,为了在公共资源上同步读者和写者之间的这种情况,已经使用了Object类的三个最终方法。

  • 等待()
  • 通知()
  • notifyAll的()

等待 :读者想要读取资源的更新值,他们注册资源对象,即当更新发生在同一个对象上时,当Writer通知它时,读者将尝试获取资源锁定并读取更新的资源。 – 当Reader有Lock Object时,只等待被调用,在我们的例子中它是资源。 – 调用wait方法后,Reader会释放Lock对象。 – 现在只有相同的注册对象(资源)阅读器才会收到通知信号。 – 如果Reader调用等待Object,这与用于发送通知的Object Writer不同,Reader将永远不会获得通知信号。 – 一旦Reader(s)被通知,现在Reader将尝试为Lock(其中一个获得锁定)内容读取更新的资源值。 类似地,其他读者也可以获得锁定并读取更新的值。 – 一旦Reader读取更新的值,执行Business Logic并从Synchronized Block中出来,Reader将释放锁定,以便其他读者可以获取它。

通知 :Writer进入同步块,获取锁执行其业务逻辑后,更新资源对象,一旦资源对象更新,它将通知正在等待同一个锁的等待线程(读者)。 – 通知信号只有一个等待线程,由底层Java线程管理器决定 – 一旦写入器信号通知(),那么它并不意味着读者立即赶紧读取更新值。 首先,编写器必须释放锁定,它将在同步块出来后执行。一旦锁定被释放并且通知等待线程,那么[如果通知()通知线程将获取锁定[由作者发布]然后进入同步块并从他离开的位置完成[即wait()之后的语句]。

Notify-All :在notifyAll中,所有使用资源锁注册的线程都将收到通知。 – 一旦触发了notifyAll(),等待同一个锁的所有线程都将获得信号并准备好争用获取锁。 – 一旦Writer完成其Job并释放Lock,任何一个Reader都将获得锁[哪个Thread,再次由底层Java Thread Manager实现决定]。 – 一旦Reader获得Lock,它将进入Synchronized Block,在那里他离开[即在wait()方法之后]执行任务并在完成Synchronized Block时释放Lock。 – 现在其他剩余的线程将尝试获取锁定,其中任何人都将获得它,进入同步块,完成其任务,然后释放锁定。 – 此过程将持续到所有注册读者完成作业。


现在我们将看到它的代码。 我们也将讨论该准则。 :

代码基本概述:它由三个类组成

  • 资源类:将获取Lock以及wait()和notify(),将调用notifyAll()。
  • ReaderTask:实现Runnable接口,暗示读者工作,想要读取资源对象的更新值。
  • WriterTask:实现Runnable接口,意味着编写器作业,将更新资源对象并通知已注册的等待线程。
  • 演示类:将创建Let say 3个读者和1个Writer线程,将各自的任务绑定到它们并启动线程。

Resource.java

  public class Resource { private String mesg; public void setMesg(String mesg){ this.mesg =mesg; } public String getMesg(){ return this.mesg; } } 

WaitThreadTask.java

 public class WaitThreadTask implements Runnable { private Resource resource; public WaitThreadTask(Resource resource){ this.resource = resource; } @Override public void run() { // TODO Auto-generated method stub synchronized(resource){ System.out.println("Before Reading Updated Value By : " +Thread.currentThread().getName() ); //We need to Take care to get the updated value, so waiting for writer thread to update value. try { //Release resource Lock & wait till any notification from Writer. resource.wait(); System.out.println("Waiting is Over For : "+ Thread.currentThread().getName()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //Read Updated Value System.out.println("Updated Value of Resource Mesg :" + resource.getMesg() + " Read By :" +Thread.currentThread().getName()); } } } 

WriterThreadTask.java

 public class WriterThreadTask implements Runnable{ private Resource resource; public WriterThreadTask(Resource resource){ this.resource = resource; } @Override public void run() { // TODO Auto-generated method stub synchronized(resource){ System.out.println("Before Updating Resource By : " + Thread.currentThread().getName()); //Updating resource Object Message resource.setMesg("Hi How are You !!!"); resource.notify(); //resource.notifyAll(); //Once Writer Comes Out from Synch Block, Readers will Content to read the values. System.out.println("Task Done By Writer Thread."); } } } 

ThreadDemo.java

 public class ThreadDemo { public static void main(String args[]){ //Create Single Resource Object, which can act as Lock on Writer and Readers. Resource lock = new Resource(); //Three Readers and One Writer runnable Tasks. Runnable taskR1 = new WaitThreadTask(lock); Runnable taskR2 = new WaitThreadTask(lock); Runnable taskR3 = new WaitThreadTask(lock); Runnable taskW1 = new WriterThreadTask(lock); Thread t1 = new Thread(taskR1, "Reader1"); Thread t2 = new Thread(taskR2, "Reader2"); Thread t3 = new Thread(taskR3, "Reader3"); Thread t4 = new Thread(taskW1, "Writer1"); t1.start(); t2.start(); t3.start(); /*try{ Thread.sleep(5000); } catch(InterruptedException e){ e.printStackTrace(); }*/ t4.start(); } } 

守则观察

  • notify()/ notifyAll()和wait():都只适用于他们已经获取的锁定对象。 例如:Synchornized(ObjectA){…… // … // ObjectB.wait()或ObjectB.notify()或ObjectB.notifyAll()…}然后它将抛出IllegalMonitorStateException。 因此必须注意必须在使用相同的lock调用上述三种方法中的任何一种之前获取锁。即使你只是编写notify()或wait()或notifyAll(),那么它仍会抛出IllegalMonitorStateException,因为[它建议锁必须可以在这个对象上获得,但事实并非如此]。
  • Reader只能接收发送相同通知的信号。 如果在Object上发生等待,而该对象与发送通知的对象不同,则读者将永远不会收到通知,因此他们将永远等待。
  • 在Writer能够发送通知之前注册的读者,只有那些读者才能得到它。 因为如果Writer首先发送通知,在读者注册到Object之前,他们将不会收到信号,因为已经错过了信号Missed Signals
  • Reader和Writer应该在同一个Object上获取Lock,并且应该在同一个Object上调用wait / notification信号 。 如果将上面的代码修改为,而不是使用资源进行锁定并等待和通知,如果我们使用它。 会发生什么 ? 好吧..所有读者都会永远等待,因为读者注册了不同的WaitThreadTask对象和编写器在WriterThreadTask上通知。 因此,读者都不会收到通知信号,因为他们注册在相应的WaitThreadTask对象上而不是在WriterThreadTask对象上接收信号。

为了澄清我的理解并提供一个示例供所有人在锁定释放时显示,我在调用notify()/ NotifyAll()之后将print语句添加到以下代码中:

 class ThreadDemo { public static void main(String[] args) { Shared s = new Shared(); new Producer(s).start(); new Consumer(s).start(); } } class Shared { private char c = '\u0000'; private boolean writeable = true; synchronized void setSharedChar(char c) { while (!writeable) try { wait(); } catch (InterruptedException e) { } this.c = c; writeable = false; notifyAll(); System.out.println("setSharedChar notify() called - still in synchronized block."); } synchronized char getSharedChar() { while (writeable) try { wait(); } catch (InterruptedException e) { } writeable = true; notifyAll(); System.out.println("getSharedChar notify() called - still in synchronized block."); return c; } } class Producer extends Thread { private Shared s; Producer(Shared s) { this.s = s; } public void run() { System.out.println("Starting producer thread."); for (char ch = 'A'; ch <= 'Z'; ch++) { System.out.println("Producer thread getting ready to create a char."); try { Thread.sleep((int) (Math.random() * 1000)); } catch (InterruptedException e) { } s.setSharedChar(ch); System.out.println(ch + " produced by producer."); } } } class Consumer extends Thread { private Shared s; Consumer(Shared s) { this.s = s; } public void run() { System.out.println("Starting consumer thread."); char ch; do { System.out.println("Consumer thread getting ready to read a char."); try { Thread.sleep((int) (Math.random() * 1000)); } catch (InterruptedException e) { } ch = s.getSharedChar(); System.out.println(ch + " consumed by consumer."); } while (ch != 'Z'); } } 

当我运行这个例子足够多次时,有一点我最终看到程序的输出显示:

 ... F produced by producer. Producer thread getting ready to create a char. getSharedChar notify() called - still in synchronized block. F consumed by consumer. Consumer thread getting ready to read a char. setSharedChar notify() called - still in synchronized block. G produced by producer. Producer thread getting ready to create a char. getSharedChar notify() called - still in synchronized block. setSharedChar notify() called - still in synchronized block. G consumed by consumer. 

由于输出getSharedChar能够出现 setSharedChar 之前 ,因此看起来锁定被立即释放或者不需要通过调用notifyAll()重新进入同步的getSharedChar()函数。 锁定可能仍然存在,但如果您可以重新输入该function,有什么区别? 我能够看到类似的输出用notify()替换notifyAll()。 这是在64位Windows 7系统上的Java 1.7.0_15上完成的。

wait():实际上Java中的每个对象都有一个监视器,要进入任何同步块,线程必须首先获取此监视器,然后才能进入这个同步块。 由于代码的关键部分一次由单个线程执行,因此它对应用程序的整体性能有很大影响。 因此,可以要求保留资源(监视器)线程以离开临界区并等待一段时间。 为了实现这种行为,Java在Object类中直接提供了wait()api。

因此,一旦线程遇到wait()API,它就会删除当前监视器以及它所持有的所有其他监视器,并转到链接当前对象的等待状态。 重要的是要理解在线程首先获取监视器的对象的上下文中进入等待状态。在概念上我解释说,每个对象都拥有一个容器所在的所有等待线程。 Thread可以通过多种方式从Object的容器中出来。 让我们来看看..

  • 当另一个线程响起并响铃一次时,在Java中通过对同一个对象调用notify()方法。
  • 当另一个线程出现并多次响铃时,其中一个线程有机会从Object的容器室中出来。 在Java中,我们可以通过在同一个对象上调用notifyAll()来完成此操作。
  • 如果我们在容器室中有线程等待的引用。 在Thread对象上调用interrupt()会将其弹出等待状态,并将其带入Object的exception块。
  • 存在重载等待(长毫秒)和等待(long millSec,int nanos)方法。 由于线程上的时间有资格退出等待状态并再次竞争对象监视器。 如果线程在超时后无法获取监视器,则它必须仅等待notify()调用。

notify():如果对象容器中有多个线程处于等待状态,则在该对象上调用notify()给予一个线程继续进行的机会。 但是在退出等待状态之后,线程仍然必须争夺对象监视器,如果它成功获取监视器,则继续执行,否则线程将返回等待状态。 因此还必须从synchronized块调用notify()。 如果未从同步上下文中调用notify(),则会出现IllegalMonitorStateException。

notifyAll():在Object上调用notifyAll()可以确保对象容器中的所有线程都被唤醒,但一旦被唤醒,它们就必须相互竞争,或者任何其他线程想要获取对象监视器。 哪个线程成功继续执行其他人必须回到等待状态并在对象容器中定居。 作为notify(),还应在同步上下文中调用notifyAll()。

解释摘自http://coder2design.com/thread-communication/

 public class ProducerConsumerInJava { public static void main(String args[]) { System.out.println("How to use wait and notify method in Java"); System.out.println("Solving Producer Consumper Problem"); Queue buffer = new LinkedList<>(); int maxSize = 10; Thread producer = new Producer(buffer, maxSize, "PRODUCER"); Thread consumer = new Consumer(buffer, maxSize, "CONSUMER"); producer.start(); consumer.start(); } } class Producer extends Thread { private Queue queue; private int maxSize; public Producer(Queue queue, int maxSize, String name){ super(name); this.queue = queue; this.maxSize = maxSize; } public void run() { while (true) { synchronized (queue) { while (queue.size() == maxSize) { try { System.out .println("Queue is full, " + "Producer thread waiting for " + "consumer to take something from queue"); queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } Random random = new Random(); int i = random.nextInt(); System.out.println("Producing value : " + i); queue.add(i); queue.notifyAll(); } } } } class Consumer extends Thread { private Queue queue; private int maxSize; public Consumer(Queue queue, int maxSize, String name){ super(name); this.queue = queue; this.maxSize = maxSize; } public void run() { while (true) { synchronized (queue) { while (queue.isEmpty()) { try { System.out .println("Queue is empty," + "Consumer thread is waiting" + " for producer thread to put something in queue"); queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } System.out.println("Consuming value : " + queue.remove()); queue.notifyAll(); } } } } 

这是消费者和生产者计划的一个例子。

执行后输出上述程序如下:

 How to use wait and notify method in Java Solving Producer Consumper Problem Queue is empty,Consumer thread is waiting for producer thread to put something in queue Producing value : -1692411980 Producing value : 285310787 Producing value : -1045894970 Producing value : 2140997307 Producing value : 1379699468 Producing value : 912077154 Producing value : -1635438928 Producing value : -500696499 Producing value : -1985700664 Producing value : 961945684 Queue is full, Producer thread waiting for consumer to take something from queue Consuming value : -1692411980 Consuming value : 285310787 Consuming value : -1045894970 Consuming value : 2140997307 Consuming value : 1379699468 Consuming value : 912077154 Consuming value : -1635438928 Consuming value : -500696499 Consuming value : -1985700664 Consuming value : 961945684 Queue is empty,Consumer thread is waiting for producer thread to put something in queue Producing value : 118213849 

因此,我们可以得出结论,notifyAll()或notify()不会释放锁。 看看输出,生成值和消费值不会交替打印,即单独打印。

因此, notify / notifyAll不会释放锁定

阅读更多: http : //javarevisited.blogspot.com/2015/07/how-to-use-wait-notify-and-notifyall-in.html#ixzz57kdToLX6

在对象上调用notify()方法会释放该对象的锁定。 但这不像调用wait()方法。

所以这是如何:

如果线程在对象上调用wait()方法,则线程IMMEDIATELY释放该对象的锁并进入等待状态。

但是当一个线程调用一个对象上的notify()方法时,该线程可能不会立即释放该对象的锁,因为该线程可能还有一些工作要做。 最后,线程将在调用notify()方法时释放对象的锁定,因为等待的线程需要对象的锁定才能在通知后继续执行。