我对等待和通知/ notifyAll感到困惑。
我知道每个java对象都有一个锁。我知道等待将释放其他线程的锁。通知/ notifyall怎么样? notify / notifyAll会释放它为其他线程持有的锁吗?
答案 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 ...
答案 3 :(得分:4)
让我们说一堆读者想要读取某些资源的更新值,这将由Writer更新。那么Reader如何知道Writer已经更新了资源字段。
因此,为了在公共资源上同步读者和写作者之间的这种情况,已经使用了三个最终的Object类方法。
等待:读者想要读取资源的更新值,他们注册资源对象,即当更新发生在同一个对象上时,当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。 - 现在其他剩余的线程将尝试获取锁定,其中任何人都将获得它,进入同步块,完成其任务然后释放锁定。 - 此过程将一直持续到所有注册读者完成作业。
现在我们将看到它的代码。我们也将讨论该准则。 :
基本概要代码:它由三个类组成
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();
}
}
代码观察:
答案 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的容器中出来。让我们看..
notify():如果对象容器中有多个线程处于等待状态,那么在该对象上调用notify()给予一个线程继续进行的机会。但是在退出等待状态之后,线程仍然必须争夺对象监视器,如果它成功获取监视器,则继续执行,否则线程将返回等待状态。因此还必须从synchronized块调用notify()。如果没有从synchronized上下文调用notify(),那么它会抛出IllegalMonitorStateException。
notifyAll():在Object上调用notifyAll()可以确保对象容器中的所有线程都被唤醒,但一旦被唤醒,它们就必须相互竞争,或者任何其他线程想要获取对象监视器。哪个线程成功继续执行其他人必须回到等待状态并在对象容器中定居。作为notify(),还应在同步上下文中调用notifyAll()。
答案 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()
方法时释放对象的锁定,因为等待的线程需要对象的锁定才能在通知后继续执行。