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

时间:2011-05-14 01:11:15

标签: java multithreading locking

我对等待和通知/ notifyAll感到困惑。

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

8 个答案:

答案 0 :(得分:33)

否 - notify / notifyAll不释放wait之类的锁。在调用notify的代码释放其锁定之前,唤醒的线程无法运行。

这就是Javadoc所说的:

  

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

答案 1 :(得分:15)

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

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

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

答案 2 :(得分:10)

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

一个例子:

Consumer类包含一个块:

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

}

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

继续

sharedObject 跟踪要求通知的线程。当某些线程调用 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 ...”将被打印出来。情况并非如此......输出显示在Producer退出同步块之后,Consumer正在使用sharedObject ...

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

答案 3 :(得分:4)

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

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

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

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

通知:Writer进入同步块,获取锁执行其业务逻辑后,更新资源对象,一旦资源对象更新,它将通知正在等待的等待线程(读者)锁。   - 仅将信号通知给一个等待的线程,该线程由底层Java线程管理器决定   - 一旦Writer发出notify()信号,那么它并不意味着Reader立即赶紧读取更新值。首先编写器必须释放锁定,它将在同步块出来后执行。一旦锁定被释放并且通知等待线程,那么[如果通知()通知线程将获取锁定[由作者发布]然后进入同步块并从他离开的位置完成[即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,因为[它建议锁必须在这个对象上获得,但情况并非如此]。
  • 阅读器只能接收发送相同通知的信号。如果在Object上发生等待,而该对象与发送通知的对象不同,则读者将永远不会收到通知,因此他们将永远等待。
  • 在Writer能够发送通知之前注册的读者,只有那些读者才能得到它。因为如果Writer首先发送通知,在读者注册到Object之前,他们将不会收到信号,因为信号已被错过:错过的信号
  • Reader and Writer应获取同一对象上的Lock,并应在同一个Object上调用等待/通知信号。如果将上面的代码修改为,而不是使用资源进行锁定并等待和通知,如果我们使用它。会发生什么 ?嗯..所有读者都会永远等待,因为读者注册了不同的WaitThreadTask对象和编写者在WriterThreadTask上通知。因此,读者都不会收到通知信号,因为他们注册接收相应的WaitThreadTask对象而不是WriterThreadTask对象上的信号。

答案 4 :(得分:0)

为了澄清我的理解并提供一个示例供所有人在锁定释放时显示,我在调用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()函数。锁定可能仍然存在,但如果您可以重新输入该功能,有什么区别? 我能够看到类似的输出用notify()替换notifyAll()。这是在64位Windows 7系统上的Java 1.7.0_15上完成的。

答案 5 :(得分:0)

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

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

  • 当另一个线程响起并响铃一次时,在Java中通过调用 对同一个对象的notify()方法。
  • 当另一个线程出现并多次响铃时,其中一个线程 有机会从Object的集装箱房出来。在Java中我们可以 通过在同一个对象上调用notifyAll()来完成此操作。
  • 如果我们在集装箱房中有线程等待的参考。 在Thread对象上调用interrupt()会使其退出等待状态 并将它带给Object的异常块。
  • 有超载等待(长毫秒)和等待(长毫秒, int nanos)方法。由于线程上的时间有资格到来 退出等待状态并再次进行对象监视器竞赛。如果 线程无法在超时后获取监视器,然后它必须 只等待notify()调用。

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

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

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

答案 6 :(得分:0)

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<Integer> 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<Integer> queue; 
   private int maxSize; 
   public Producer(Queue<Integer> 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<Integer> queue; 
   private int maxSize; 
   public Consumer(Queue<Integer> 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

答案 7 :(得分:-3)

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

所以这里是这样的:

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

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