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

icn*_*icn 24 java multithreading locking

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

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

Dan*_*rov 34

不 - notify/ notifyAll不要像wait那样释放锁.唤醒的线程无法运行,直到调用的代码notify释放其锁定.

这就是Javadoc所说的:

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

  • 我的理解是他们必须等待锁正常释放.所以如果我们做`synchronized(x){x.notifyAll(); FOO(); 在`foo()`完成之前,等待线程无法运行. (9认同)
  • 谢谢,但根据我的阅读,notify/notifyall会唤醒正在等待的线程,如果notigy/notifyall没有释放锁,等待的线程如何检测已经更改的内容? (2认同)
  • @Daniel有效是的,但准确地说,一个等待线程无法运行,直到当前执行的线程离开同步块(即最后一个`}`) - 这当然是FOO后`()`完成(或其他任何东西在那个街区). (2认同)

Was*_*RAR 15

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

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

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

  • `notify()`将唤醒**正在等待对象的**线程,**不一定是**第一个线程. (4认同)
  • 编写良好的代码应该在通知后快速释放锁,但是没有要求......任何代码都可以放在notify和synchronized块的末尾之间(或者方法结束,如果它是同步方法). (2认同)

lui*_*7up 11

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

一个例子:

Consumer class包含一个块:

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

}

场景:Consumer类获取对sharedObject对象的锁定,独占进入(它在同步块内部),并且看到sharedObject还没有准备就绪(没有消耗:))并且它wait()sharedObject上调用方法.这样它就会释放锁(在那里停止执行!)并等待另一个线程(生产者可能)调用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...");
Run Code Online (Sandbox Code Playgroud)

}

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

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


bha*_*atj 6

假设一群读者想要读取某些资源的更新值,该值将由 Writer 更新。那么 Reader 怎么知道 Resource Fields 已经被 Writer 更新了。

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

  • 等待()
  • 通知()
  • 通知所有()

等待: 读者想要读取资源的更新值,他们向资源对象注册,即当更新发生在同一对象上时,当 Writer 通知它时,读者将尝试获取资源锁并读取更新的资源。- 等待仅当 Reader 有锁定对象时被调用,在我们的例子中它是资源。- 一旦调用了wait 方法,Reader 就会释放锁定对象。- 现在只有相同的注册对象(资源)阅读器才会收到通知信号。- 如果Reader 调用wait on Object,这与用于发送通知的Object Writer 不同,Reader 将永远不会收到通知信号。- 一旦 Reader(s) 被通知,现在 Reader(s) 将尝试对锁的内容(其中一个获得锁)读取资源的更新值。类似地,其他 Readers 也轮流获取锁并读取更新的值。

Notify : Writer 进入 Synchronized Block,获取锁后执行他的业务逻辑,更新资源对象,一旦资源对象更新,就会通知正在等待同一个锁的等待线程(读者)。- 仅向一个等待的线程发出通知信号,这是由底层 Java 线程管理器决定的 - 一旦 Writer 发出信号通知(),那么这并不意味着 Reader 立即匆忙读取更新值。首先 writer 必须释放 Lock,一旦它从 Synchronized block 出来就会释放 Lock。一旦 Lock 被释放并通知等待的线程,然后 [In case of notify()] 通知 Thread 将获取 Lock[Released by Writer]然后进入同步块并从他离开的地方完成[即wait()之后的语句]。

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


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

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

  • 资源类:将在其上获取 Lock 并调用 wait() 和 notify()、notifyAll()。
  • ReaderTask : 实现 Runnable 接口,暗示读者作业,想要读取资源对象的更新值。
  • WriterTask :实现 Runnable 接口,隐含写入器作业,将更新资源对象并通知已注册的等待线程。
  • 演示类:这将创建假设 3 个读者和 1 个编写者线程,将各自的任务绑定到它们并启动线程。

资源.java

   public class Resource {
      private String mesg;

          public void setMesg(String mesg){
         this.mesg =mesg;
      }
      public String getMesg(){
         return this.mesg;
      }
    }
Run Code Online (Sandbox Code Playgroud)

等待线程任务

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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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.");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

线程演示程序

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();
    }

}
Run Code Online (Sandbox Code Playgroud)

代码观察

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