Java 线程:等待和通知方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5121173/
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
Java threads: wait and notify methods
提问by satheesh
I have a thread that calls the wait
method and can only be awoken when the notify
method called from some other class:
我有一个调用该wait
方法的线程,并且只能在notify
从其他类调用该方法时被唤醒:
class ThreadA {
public static void main(String [] args) {
ThreadB b = new ThreadB();
b.start();
synchronized(b) {
try {
System.out.println("Waiting for b to complete...");
b.wait();
} catch (InterruptedException e) {}
System.out.println("Total is: " + b.total);
}
}
}
class ThreadB extends Thread {
int total;
public void run() {
synchronized(this) {
for(int i=0;i<100;i++) {
total += i;
}
notify();
}
}
}
In the above code if the synchronized
block in main
, if the ThreadA
does not execute first and instead the other synchronization block executing and completes to completion, then ThreadA
executes its synchronized
block and calls wait
, what is going to happen and how it will be notified again?
在上面的代码中,如果 中的synchronized
块main
,如果ThreadA
没有先执行而是另一个同步块执行并完成到完成,然后ThreadA
执行它的synchronized
块并调用wait
,会发生什么以及如何再次通知它?
回答by templatetypedef
If ThreadB
gets through its synchronized
block before ThreadA
does, then ThreadA
will block indefinitely on the call to wait
. It won't somehow be notified that the other thread has already completed.
如果ThreadB
在synchronized
do 之前通过它的块ThreadA
,那么ThreadA
将无限期地阻塞对 的调用wait
。不会以某种方式通知另一个线程已经完成。
The problem is that you're trying to use wait
and notify
in ways that they are not designed to be used. Usually, wait
and notify
are used to have one thread wait until some condition is true, and then to have another thread signal that the condition may have become true. For example, they're often used as follows:
问题是,你要使用wait
,并notify
在方式,他们没有设计使用。通常,wait
和notify
用于让一个线程等待直到某个条件为真,然后让另一个线程通知该条件可能已成为真。例如,它们通常如下使用:
/* Producer */
synchronized (obj) {
/* Make resource available. */
obj.notify();
}
/* Consumer */
synchronized (obj) {
while (/* resource not available */)
obj.wait();
/* Consume the resource. */
}
The reason that the above code works is that it doesn't matter which thread runs first. If the producer thread creates a resource and no one is wait
ing on obj
, then when the consumer runs it will enter the while
loop, notice that the resource has been produced, and then skip the call to wait
. It can then consume the resource. If, on the other hand, the consumer runs first, it will notice in the while
loop that the resource is not yet available and will wait
for some other object to notify it. The other thread can then run, produce the resource, and notify
the consumer thread that the resource is available. Once the original thread is awoken, it will notice that the condition of the loop is no longer true and will consume the resource.
上面代码起作用的原因是哪个线程先运行并不重要。如果生产者线程创建了一个资源并且没有人在wait
ing obj
,那么当消费者运行时它会进入while
循环,注意到资源已经生产,然后跳过对 的调用wait
。然后它可以消耗资源。另一方面,如果消费者首先运行,它会在while
循环中注意到资源尚不可用,并将wait
让其他对象通知它。另一个线程然后可以运行,生产资源,并且notify
资源可用的消费者线程。一旦原线程被唤醒,它会注意到循环的条件不再为真,会消耗资源。
More generally, Java suggests that you always call wait
in a loop because of spurious notificationsin which a thread can wake up from a call to wait
without ever being notified of anything. Using the above pattern can prevent this.
更一般地说,Java 建议您总是wait
在循环中调用,因为在虚假通知中,线程可以从调用中唤醒,wait
而不会收到任何通知。使用上述模式可以防止这种情况。
In your particular instance, if you want to ensure that ThreadB
has finished running before ThreadA
executes, you may want to use Thread.join()
, which explicitly blocks the calling thread until some other thread executes. More generally, you may want to look into some of the other synchronization primitives provided by Java, as they often are much easier to use than wait
and notify
.
在您的特定实例中,如果您想确保ThreadB
在ThreadA
执行之前已完成运行,您可能需要使用Thread.join()
,它会显式阻止调用线程,直到其他线程执行。更一般地说,您可能希望查看 Java 提供的其他一些同步原语,因为它们通常比wait
和更易于使用notify
。
回答by JB Nizet
You could loop and wait until the total has been computed :
您可以循环并等待,直到计算出总数:
synchronized(b) {
while (total == 0) {
b.wait();
}
}
You could also use a higher-level abstraction like a CountDownLatch.
您还可以使用更高级别的抽象,例如CountDownLatch。
回答by CalloRico
It is possible for ThreadB's run method to complete before you enter the synchronized block in ThreadA.main. In that situation, since the notify call has happened before you started waiting, ThreadA will block forever on the wait call.
ThreadB 的 run 方法有可能在您进入 ThreadA.main 中的同步块之前完成。在这种情况下,由于通知调用在您开始等待之前已经发生,线程 A 将永远阻塞等待调用。
A simple workaround would be to grab the lock on b in main before you start the second thread to ensure the wait happens first.
一个简单的解决方法是在启动第二个线程之前在 main 中获取 b 上的锁,以确保首先发生等待。
ThreadB b = new ThreadB();
synchronized(b) {
b.start();
...
b.wait();
}
回答by user3329624
Why to make that complex ? Just use join() function of Thread.
为什么要搞得那么复杂?只需使用 Thread 的 join() 函数。
ThreadB b = new ThreadB();
b.start();
b.join();
// now print b.total
回答by Jesse Barnum
1) You need to add some flag that is used to communicate between the threads, so that B can signal to A when it is finished. A simple boolean variable is fine, as long as it is only read and written within the synchronized blocks.
1)您需要添加一些用于线程之间通信的标志,以便B可以在完成时向A发出信号。一个简单的布尔变量就可以了,只要它只在同步块内被读取和写入。
synchronized(this) {
for(int i=0;i<100;i++) {
total += i;
}
isDone = true;
notify();
}
2) A needs to loop while waiting. So if your boolean variable was called isDone, and was set to true by threadB, then threadA should have some code like this:
2) A 需要在等待时循环。因此,如果您的布尔变量被称为 isDone,并且被线程 B 设置为 true,那么线程 A 应该有一些像这样的代码:
synchronized(b) {
System.out.println("Waiting for b to complete...");
while( ! isDone ) b.wait();
}
In this particular case, there's actually no reason to have the synchronized block in A - since threadB doesn't do anything after it finishes running, and A doesn't do anything except wait for B, threadA could simply call b.join() to block until it finishes. I assume that your actual use case is more complex than this.
在这种特殊情况下,实际上没有理由在 A 中使用同步块 - 因为线程 B 在完成运行后不做任何事情,而 A 除了等待 B 之外什么也不做,线程 A 可以简单地调用 b.join()阻塞直到它完成。我假设您的实际用例比这更复杂。
回答by Aron
You probably want to use a java.util.concurrent.Semaphore for this.
您可能想为此使用 java.util.concurrent.Semaphore。
回答by bestsss
do not synchronized(thread)
, don't do it, do not synchronized(thread)
.. repat: no synchronized(thread)
:)
不要synchronized(thread)
,不要这样做,不要synchronized(thread)
.. repat:不synchronized(thread)
:)
And if you need to wait for the thread 'b' to finish, use b.join(), now your code is free to hang in b.wait()
如果您需要等待线程 'b' 完成,请使用 b.join(),现在您的代码可以自由挂在 b.wait() 中
--
——
Hopefully the source below can grant you an insight while sync(thread)/notify() I consider bad practice. (cut-cut)
希望下面的来源可以让您深入了解同步(线程)/通知()我认为是不好的做法。(切切)
Enjoy
享受
To proceeed below you make sure you have accepted Oracle's License aggreement, found there:https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewLicense-Start?LicenseUUID=7HeJ_hCwhb4AAAEtmC8ADqmR&ProductUUID=pGqJ_hCwj_AAAAEtB8oADqmS&cnum=&evsref=&sln=
要进行以下操作,请确保您已接受 Oracle 的许可协议,请在此处找到:https ://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewLicense-开始?LicenseUUID=7HeJ_hCwhb4AAAEtmC8ADqmR&ProductUUID=pGqJ_hCwj_AAAAEtB8oADqmS&cnum=&evsref=&sln=
Java sources (incl), called in init(), effectively called by any java c-tor, since java 1.5
Java 源代码 (incl),在 init() 中调用,从 java 1.5 开始被任何 java c-tor 有效调用
private static **synchronized int** nextThreadNum() {
return threadInitNumber++;
}
//join (the method w/ nanos only increase millis by one, if nanos>500000, millis==0 and nanos>0
//加入(使用 nanos 的方法只增加 1 毫秒,如果 nanos>500000,millis==0 和 nanos>0
public final **synchronized** void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
public **synchronized** void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
start0();
if (stopBeforeStart) {
stop0(throwableFromStop);
}
}
//stop1 is called after stop ensures proper priviledges
//stop1 在 stop 之后调用以确保适当的权限
private final **synchronized** void stop1(Throwable th) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
checkAccess();
if ((this != Thread.currentThread()) ||
(!(th instanceof ThreadDeath))) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
// A zero status value corresponds to "NEW"
if (threadStatus != 0) {
resume(); // Wake up thread if it was suspended; no-op otherwise
stop0(th);
} else {
// Must do the null arg check that the VM would do with stop0
if (th == null) {
throw new NullPointerException();
}
// Remember this stop attempt for if/when start is used
stopBeforeStart = true;
throwableFromStop = th;
}
}