Java 如何在不是线程的对象上调用 wait() 和 notify() 方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16197135/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can the wait() and notify() methods be called on Objects that are not threads?
提问by Alexander Mills
How can the wait()
and notify()
methods be called on Objects that are not Threads? That doesn't really make sense, does it?
如何在不是线程的对象上调用wait()
和notify()
方法?这真的没有意义,不是吗?
Surely, it must make sense, however, because the two methods are available for all Java objects. Can someone provide an explanation? I am having trouble understanding how to communicate between threads using wait()
and notify()
.
当然,这一定是有意义的,因为这两种方法可用于所有 Java 对象。有人可以提供解释吗?我无法理解如何使用wait()
和来在线程之间进行通信notify()
。
采纳答案by Nathan Hughes
Locking is about protecting shared data.
锁定是关于保护共享数据。
The lock is on the data structure being protected. The threads are the things accessing the data structure. The locks are on the data structure object in order to keep the threads from accessing the data structure in an unsafe way.
锁在被保护的数据结构上。线程是访问数据结构的东西。锁位于数据结构对象上,以防止线程以不安全的方式访问数据结构。
Any object can be used as an intrinsic lock (meaning used in conjunction with synchronized
). This way you can guard access to any object by adding the synchronized modifier to the methods that access the shared data.
任何对象都可以用作内在锁(意味着与 结合使用synchronized
)。通过这种方式,您可以通过向访问共享数据的方法添加同步修饰符来保护对任何对象的访问。
The wait
and notify
methods are called on objects that are being used as locks. The lock is a shared communication point:
该wait
和notify
方法被称为上正在使用的锁对象。锁是一个共享的通信点:
When a thread that has a lock calls
notifyAll
on it, the other threads waiting on that same lock get notified. When a thread that has a lock callsnotify
on it, one of the threads waiting on that same lock gets notified.When a thread that has a lock calls
wait
on it, the thread releases the lock and goes dormant until either a) it receives a notification, or b) it just wakes up arbitrarily (the "spurious wakeup"); the waiting thread remains stuck in the call to wait until it wakes up due to one of these 2 reasons, then the thread has to re-acquire the lock before it can exit the wait method.
当一个拥有锁的线程调用
notifyAll
它时,其他等待同一个锁的线程会得到通知。当一个拥有锁的线程调用notify
它时,等待同一个锁的线程之一会得到通知。当有锁
wait
的线程调用它时,该线程释放锁并进入休眠状态,直到 a) 它收到通知,或 b) 它只是任意唤醒(“虚假唤醒”);由于这两个原因之一,等待线程一直停留在等待调用中,直到它被唤醒,然后线程必须重新获取锁才能退出等待方法。
See the Oracle tutorial on guarded blocks, the Drop class is the shared data structure, threads using the Producer and Consumer runnables are accessing it. Locking on the Drop object controls how the threads access the Drop object's data.
请参阅有关受保护块的Oracle 教程,Drop 类是共享数据结构,使用生产者和消费者可运行的线程正在访问它。锁定 Drop 对象控制线程如何访问 Drop 对象的数据。
Threads get used as locks in the JVM implementation, application developers are advised to avoid using threads as locks. For instance, the documentation for Thread.joinsays:
线程在 JVM 实现中被用作锁,建议应用程序开发人员避免将线程用作锁。例如,Thread.join的文档说:
This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.
此实现使用以 this.isAlive 为条件的 this.wait 调用循环。当线程终止时,调用 this.notifyAll 方法。建议应用程序不要在 Thread 实例上使用 wait、notify 或 notifyAll。
Java 5 introduced explicit locks implementing java.util.concurrent.locks.Lock
. These are more flexible than the implicit locks; there are methods analogous to wait and notify (await and signal), but they are on the Condition, not on the lock. Having multiple conditions makes it possible to target only those threads waiting for a particular type of notification.
Java 5 引入了显式锁实现java.util.concurrent.locks.Lock
。这些比隐式锁更灵活;有类似于等待和通知(等待和信号)的方法,但它们是在条件上,而不是在锁上。具有多个条件可以仅针对等待特定类型通知的线程。
回答by Alex
You can use wait()
and notify()
to synchronize your logic. As an example
您可以使用wait()
和notify()
来同步您的逻辑。举个例子
synchronized (lock) {
lock.wait(); // Will block until lock.notify() is called on another thread.
}
// Somewhere else...
...
synchronized (lock) {
lock.notify(); // Will wake up lock.wait()
}
with lock
being the class member Object lock = new Object();
与lock
属于分类部件Object lock = new Object();
回答by Nicolas HENAUX
In Java all Object implements these two methods, obviously if there are not a monitor those two methods are useless.
在 Java 中所有 Object 都实现了这两个方法,显然如果没有监视器这两个方法是没有用的。
回答by 323
"This method should only be called by a thread that is the owner of this object's monitor." So I think you must make sure there is a thread who is the monitor on the object.
“此方法只能由作为此对象监视器所有者的线程调用。” 所以我认为你必须确保有一个线程是对象上的监视器。
回答by Petr Shypila
You can stop your thread for time as you want using static Thread
class method sleep()
.
您可以根据需要使用静态Thread
类方法停止线程sleep()
。
public class Main {
//some code here
//Thre thread will sleep for 5sec.
Thread.sleep(5000);
}
If you want to stop some objects you need to call this method's within syncronized
blocks.
如果要停止某些对象,则需要在syncronized
块内调用此方法。
public class Main {
//some code
public void waitObject(Object object) throws InterruptedException {
synchronized(object) {
object.wait();
}
}
public void notifyObject(Object object) throws InterruptedException {
synchronized(object) {
object.notify();
}
}
}
}
P.S. I'm sory if I wrong understand your question (English is not my native)
PS如果我理解错了你的问题,我很抱歉(英语不是我的母语)
回答by luke657
When you put some code inside synchronized block:
当你在 synchronized 块中放入一些代码时:
sychronized(lock){...}
a thread wanting to perform whatever is inside this block first acquires a lock on an object and only one thread at a time can execute the code locked on the same object. Any object can be used as a lock but you should be careful to choose the object relevant to the scope. For example when you have multiple threads adding something to the account and they all have some code responsible for that inside a block like:
想要执行此块内任何内容的线程首先获取对象上的锁,并且一次只有一个线程可以执行锁定在同一对象上的代码。任何对象都可以用作锁,但您应该小心选择与作用域相关的对象。例如,当您有多个线程向帐户添加内容时,它们都有一些代码负责在一个块中执行此操作,例如:
sychronized(this){...}
then no synchronization takes place because they all locked on different object. Instead you should use an account object as the lock. Now consider that these threads have also method for withdrawing from an account. In this case a situation may occur where a thread wanting to withdraw something encounters an empty account. It should wait until there's some money and release the lock to other threads to avoid a deadlock. That's what wait and notify methods are for. In this example a thread that encounters an empty account releases the lock and waits for the signal from some thread that makes the deposit:
然后没有同步发生,因为它们都锁定在不同的对象上。相反,您应该使用帐户对象作为锁。现在考虑这些线程也有从帐户中提取的方法。在这种情况下,可能会出现想要提取某些东西的线程遇到空帐户的情况。它应该等到有一些钱并释放锁给其他线程以避免死锁。这就是等待和通知方法的用途。在这个例子中,遇到空账户的线程释放锁并等待来自某个进行存款的线程的信号:
while(balance < amountToWithdraw){
lock.wait();
}
When other thread deposits some money, it signals other threads waiting on the same lock. (of course, code responsible for making deposits and withdrawals has to be synchronized on the same lock for this to work and to prevent data corruption).
当其他线程存入一些钱时,它会向等待同一锁的其他线程发出信号。(当然,负责存款和取款的代码必须在同一个锁上同步才能工作并防止数据损坏)。
balance += amountToDeposit;
lock.signallAll;
As you see the methods wait and notify only make sense inside synchronized blocks or methods.
如您所见,方法 wait 和 notify 仅在同步块或方法中有意义。
回答by Premraj
- Wait and notify is not just normal methods or synchronization utility, more than that they are communication mechanism between two threads in Java. And Object class is correct place to make them available for every object if this mechanism is not available via any java keyword like synchronized. Remember synchronized and wait notify are two different area and don't confuse that they are same or related. Synchronized is to provide mutual exclusion and ensuring thread safety of Java class like race condition while wait and notify are communication mechanism between two thread.
- Locks are made available on per Object basis, which is another reason wait and notify is declared in Object class rather then Thread class.
- In Java in order to enter critical section of code, Threads needs lock and they wait for lock, they don't know which threads holds lock instead they just know the lock is hold by some thread and they should wait for lock instead of knowing which thread is inside the synchronized block and asking them to release lock. this analogy fits with wait and notify being on object class rather than thread in Java.
- Wait 和 notify 不仅仅是普通的方法或同步实用程序,更重要的是它们是 Java 中两个线程之间的通信机制。如果此机制无法通过任何 java 关键字(如同步)使用,则对象类是使它们可用于每个对象的正确位置。请记住同步和等待通知是两个不同的领域,不要混淆它们是相同或相关的。同步是提供互斥和保证Java 类的线程安全,如竞争条件,而wait 和notify 是两个线程之间的通信机制。
- 锁是基于每个对象提供的,这是在 Object 类而不是 Thread 类中声明 wait 和 notify 的另一个原因。
- 在 Java 中,为了进入代码的临界区,线程需要锁并等待锁,它们不知道哪个线程持有锁,而只知道锁被某个线程持有,它们应该等待锁而不是知道哪个线程持有锁线程在同步块内并要求他们释放锁。这个类比适合等待和通知在对象类上而不是 Java 中的线程。
Analogy :a Java thread is a user and the toilet is a block of code which the thread wishes to execute. Java provides a way to lock the code for a thread which is currently executing it using the synchorinized keywokd, and making other threads that wish to use it wait until the first thread is finished. These other threads are placed in the waiting state. Java is NOT AS FAIR as the service station because there is no queue for waiting threads. Any one of the waiting threads may get the monitor next, regardless of the order they asked for it. The only guarantee is that all threads will get to use the monitored code sooner or later.
类比:Java 线程是用户,厕所是线程希望执行的代码块。Java 提供了一种使用同步密钥锁定当前正在执行它的线程的代码的方法,并使希望使用它的其他线程等待直到第一个线程完成。这些其他线程处于等待状态。Java 不像服务站那样公平,因为没有等待线程的队列。任何一个等待线程都可以获取下一个监视器,而不管它们要求的顺序如何。唯一的保证是所有线程迟早都会使用受监控的代码。
If you look at the following producer and consumer code:sharedQueue
Object acts inter-thread communication between producer and consumer
threads.
如果查看以下生产者和消费者代码:sharedQueue
对象充当线程之间的producer and consumer
线程间通信。
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ProducerConsumerSolution {
public static void main(String args[]) {
Vector<Integer> sharedQueue = new Vector<Integer>();
int size = 4;
Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer");
Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer");
prodThread.start();
consThread.start();
}
}
class Producer implements Runnable {
private final Vector<Integer> sharedQueue;
private final int SIZE;
public Producer(Vector<Integer> sharedQueue, int size) {
this.sharedQueue = sharedQueue;
this.SIZE = size;
}
@Override
public void run() {
for (int i = 0; i < 7; i++) {
System.out.println("Produced: " + i);
try {
produce(i);
} catch (InterruptedException ex) {
Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void produce(int i) throws InterruptedException {
// wait if queue is full
while (sharedQueue.size() == SIZE) {
synchronized (sharedQueue) {
System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: "
+ sharedQueue.size());
sharedQueue.wait();
}
}
// producing element and notify consumers
synchronized (sharedQueue) {
sharedQueue.add(i);
sharedQueue.notifyAll();
}
}
}
class Consumer implements Runnable {
private final Vector<Integer> sharedQueue;
private final int SIZE;
public Consumer(Vector<Integer> sharedQueue, int size) {
this.sharedQueue = sharedQueue;
this.SIZE = size;
}
@Override
public void run() {
while (true) {
try {
System.out.println("Consumed: " + consume());
Thread.sleep(50);
} catch (InterruptedException ex) {
Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private int consume() throws InterruptedException {
//wait if queue is empty
while (sharedQueue.isEmpty()) {
synchronized (sharedQueue) {
System.out.println("Queue is empty " + Thread.currentThread().getName()
+ " is waiting , size: " + sharedQueue.size());
sharedQueue.wait();
}
}
//Otherwise consume element and notify waiting producer
synchronized (sharedQueue) {
sharedQueue.notifyAll();
return (Integer) sharedQueue.remove(0);
}
}
}
回答by Ishan Thilina Somasiri
Think using a real life example, a washroom. When you want to use the washroom at your office, you have two options to make sure no one else will come to the washroom once you are using it.
想想使用一个现实生活中的例子,一个洗手间。当您想在办公室使用洗手间时,您有两种选择,以确保一旦您使用它,就没有其他人会来洗手间。
- Lock the washroom door, so everyone else will know that it's used by someone else when they try to open the door
- Go to each person in the office, lock them to their chairs (or table, or whatever), go to washroom.
- 锁上洗手间的门,这样其他人在试图打开门时就会知道它被其他人使用了
- 去找办公室里的每个人,把他们锁在椅子(或桌子,或其他什么)上,去洗手间。
Which option would you take?
你会选择哪个?
Yes, it's the same in the Javaland!.
是的,Javaland 也是一样!
So in the above story,
所以在上面的故事中,
- Washroom = Object you want to lock (that only you need to use)
- Your staff colleagues = other threads that you want to keep out
- 洗手间 = 您要锁定的对象(只有您需要使用)
- 您的员工同事 = 您想排除的其他线程
So just like in real life, when you have some private business, you lock that object. And when you are done with that object, you let go of the lock!.
所以就像在现实生活中一样,当你有一些私人事务时,你锁定那个对象。当您完成该对象时,您松开锁!
(Yes yes!, this is a very simple description on what happens. Of course the real concept is slightly different from this, but this is a starting point)
(是的,是的!,这是对发生的事情的非常简单的描述。当然真正的概念与此略有不同,但这是一个起点)
回答by jean
Actually, wait
, notify
member function should not belong to thread, the thing it should belong to name as condition variablewhich comes from posix thread. And you can have a look how cpp wrap this, it wrap it into a dedicated class std::condition_variable.
其实wait
,notify
成员函数不应该属于线程,它应该属于的东西命名为条件变量它来自POSIX线程。你可以看看 cpp 如何包装它,它将它包装成一个专用的类std::condition_variable。
Java haven't do this kind encapsulation, instead, it wrap condition variable in more high level way: monitor(put the functionality into Object class directly).
Java 没有做这种封装,而是用更高级的方式包装条件变量:monitor(直接把功能放到Object类中)。
If you don't know monitor or condition variable, well, that indeed make people confused at the beginning.
如果您不知道监视器或条件变量,那么一开始确实会让人们感到困惑。