一个线程可以使用Object.wait()
来阻塞,直到另一个线程在该对象上调用notify()
或notifyAll()
。Java:线程如何等待多个对象?
但是如果一个线程想等到多个对象之一发出信号呢?例如,我的线程必须等到或者 a)字节可用于从InputStream
读取或者b)将项目添加到ArrayList
。
线程如何等待这些事件中的任何一个发生?
编辑
This question涉及等待多个线程完成 - 我的案子涉及到一个线程等待许多对象之一被singnaled。
一个线程可以使用Object.wait()
来阻塞,直到另一个线程在该对象上调用notify()
或notifyAll()
。Java:线程如何等待多个对象?
但是如果一个线程想等到多个对象之一发出信号呢?例如,我的线程必须等到或者 a)字节可用于从InputStream
读取或者b)将项目添加到ArrayList
。
线程如何等待这些事件中的任何一个发生?
编辑
This question涉及等待多个线程完成 - 我的案子涉及到一个线程等待许多对象之一被singnaled。
线程无法一次在多个对象上等待。
wait()
和notify()
方法是对象特定的。 wait()
方法挂起当前执行线程,并通知对象跟踪挂起的线程。方法notify()
通知对象唤醒当前正在跟踪的暂停线程。
有用的链接:Can a thread call wait() on two locks at once in Java (6) ?
将两种情况都锁定在同一个对象上。在情况a)时或在情况b)通知()时对同一个对象进行调用。
他们都可以使用相同的互斥锁。您的消费者正在等待该互斥锁,当第一个可以继续时,另一个互斥体会通知该互斥锁。
@TimBüthe:我不认为这是有效的,因为当通知线程无法区分条件(a)和(b)时,即它不知道'InputStream'中是否有更多字节可用或一个项目已被添加到'ArrayList' – MarcoS 2011-06-07 12:46:52
@MarcoS当然,它可以区分,但这不是同步的一部分。例如,您可以在某个地方,某条消息或类似地点持有状态。 – 2011-06-07 12:52:59
@MarcoS:好吧,你可以在通知等待线程之前设置一些标志... – 2011-06-07 13:08:23
你是为了一个痛苦的世界。使用更高级别的抽象,例如阻塞消息队列,线程可以从中使用诸如“可用的更多字节”或“添加的项目”之类的消息。
这就是它应该是... – bestsss 2011-06-07 13:49:52
您只能在一台显示器上等待。所以通知者必须通知这一个监视器。在这种低级同步中没有其他方法。
小晚,但它是一个非常有趣的问题! 似乎你可以确实等待多个条件,具有相同的性能,并且不需要额外的线程;这只是一个定义问题的问题!我花时间在代码的提交中写下更详细的解释。通过请求我会提取抽象:
所以实际上等待多个对象,就等于在多个条件下等待。但下一步是将你的子条件合并成一个条件一个单一的条件。当条件的任何组件都会导致它成为真时,您会翻转一个布尔值,并通知锁(就像任何其他等待通知条件一样)。
我的做法:
对于任何条件下,它只能导致两个值(true和false)。价值如何产生是无关紧要的。在你的情况下,你的“功能条件”是当两个值中的任何一个为真时:(value_a || value_b)。我把这个“功能条件”称为“连结点”。如果你应用任何复杂条件(无论多么复杂)的观点,总会产生一个简单的结果(真或假),那么你真正要求的是“什么会导致我的净条件成为现实?” (假设逻辑是“等到真”)。因此,当一个线程导致你的条件组件变为真(在你的情况下将value_a或value_b设置为true),并且你知道它会导致你想要的网络条件得到满足,那么你可以简化你的接近古典(因为它翻转单个布尔标志,并释放一个锁)。有了这个概念,你可以申请一个物体的坐标的方法,帮助帮助你的整体逻辑清晰:
import java.util.HashSet;
import java.util.Set;
/**
* The concept is that all control flow operation converge
* to a single value: true or false. In the case of N
* components in which create the resulting value, the
* theory is the same. So I believe this is a matter of
* perspective and permitting 'simple complexity'. for example:
*
* given the statement:
* while(condition_a || condition_b || ...) { ... }
*
* you could think of it as:
* let C = the boolean -resulting- value of (condition_a || condition_b || ...),
* so C = (condition_a || condition_b || ...);
*
* Now if we were to we-write the statement, in lamest-terms:
* while(C) { ... }
*
* Now if you recognise this form, you'll notice its just the standard
* syntax for any control-flow statement?
*
* while(condition_is_not_met) {
* synchronized (lock_for_condition) {
* lock_for_condition.wait();
* }
* }
*
* So in theory, even if the said condition was evolved from some
* complex form, it should be treated as nothing more then if it
* was in the simplest form. So whenever a component of the condition,
* in which cause the net-condition (resulting value of the complex
* condition) to be met, you would simply flip the boolean and notify
* a lock to un-park whoever is waiting on it. Just like any standard
* fashion.
*
* So thinking ahead, if you were to think of your given condition as a
* function whos result is true or false, and takes the parameters of the states
* in which its comprised of ( f(...) = (state_a || state_b && state_c), for example)
* then you would recognize "If I enter this state, in which this I know would
* cause that condition/lock to become true, I should just flip the switch switch,
* and notify".
*
* So in your example, your 'functional condition' is:
* while(!state_a && !state_b) {
* wait until state a or state b is false ....
* }
*
* So armed with this mindset, using a simple/assertive form,
* you would recognize that the overall question:
* -> What would cause my condition to be true? : if state_a is true OR state_b is true
* Ok... So, that means: When state_a or state_b turn true, my overall condition is met!
* So... I can just simplify this thing:
*
* boolean net_condition = ...
* final Object lock = new Lock();
*
* void await() {
* synchronized(lock) {
* while(!net_condition) {
* lock.wait();
* }
* }
* }
*
* Almighty, so whenever I turn state_a true, I should just flip and notify
* the net_condition!
*
*
*
* Now for a more expanded form of the SAME THING, just more direct and clear:
*
* @author Jamie Meisch
*/
public class Main {
/**
*
* The equivalent if one was to "Wait for one of many condition/lock to
* be notify me when met" :
*
* synchronized(lock_a,lock_b,lock_c) {
* while(!condition_a || !condition_b || !condition_c) {
* condition_a.wait();
* condition_b.wait();
* condition_c.wait();
* }
* }
*
*/
public static void main(String... args) {
OrNexusLock lock = new OrNexusLock();
// The workers register themselves as their own variable as part of the overall condition,
// in which is defined by the OrNuxusLock custom-implement. Which will be true if any of
// the given variables are true
SpinningWarrior warrior_a = new SpinningWarrior(lock,1000,5);
SpinningWarrior warrior_b = new SpinningWarrior(lock,1000,20);
SpinningWarrior warrior_c = new SpinningWarrior(lock,1000,50);
new Thread(warrior_a).start();
new Thread(warrior_b).start();
new Thread(warrior_c).start();
// So... if any one of these guys reaches 1000, stop waiting:
//^As defined by our implement within the OrNexusLock
try {
System.out.println("Waiting for one of these guys to be done, or two, or all! does not matter, whoever comes first");
lock.await();
System.out.println("WIN: " + warrior_a.value() + ":" + warrior_b.value() + ":" + warrior_c.value());
} catch (InterruptedException ignored) {
}
}
// For those not using Java 8 :)
public interface Condition {
boolean value();
}
/**
* A variable in which the net locks 'condition function'
* uses to determine its overall -net- state.
*/
public static class Variable {
private final Object lock;
private final Condition con;
private Variable(Object lock, Condition con) {
this.lock = lock;
this.con = con;
}
public boolean value() {
return con.value();
}
//When the value of the condition changes, this should be called
public void valueChanged() {
synchronized (lock) {
lock.notifyAll();
}
}
}
/**
*
* The lock has a custom function in which it derives its resulting
* -overall- state (met, or not met). The form of the function does
* not matter, but it only has boolean variables to work from. The
* conditions are in their abstract form (a boolean value, how ever
* that sub-condition is met). It's important to retain the theory
* that complex conditions yeild a simple result. So expressing a
* complex statement such as (field * 5 > 20) results in a simple
* true or false value condition/variable is what this approach is
* about. Also by centerializing the overal logic, its much more
* clear then the raw -simplest- form (listed above), and just
* as fast!
*/
public static abstract class NexusLock {
private final Object lock;
public NexusLock() {
lock = new Object();
}
//Any complex condition you can fathom!
//Plus I prefer it be consolidated into a nexus point,
// and not asserted by assertive wake-ups
protected abstract boolean stateFunction();
protected Variable newVariable(Condition condition) {
return new Variable(lock, condition);
}
//Wait for the overall condition to be met
public void await() throws InterruptedException {
synchronized (lock) {
while (!stateFunction()) {
lock.wait();
}
}
}
}
// A implement in which any variable must be true
public static class OrNexusLock extends NexusLock {
private final Set<Variable> vars = new HashSet<>();
public OrNexusLock() {
}
public Variable newVar(Condition con) {
Variable var = newVariable(con);
vars.add(var); //register it as a general component of or net condition // We should notify the thread since our functional-condition has changed/evolved:
synchronized (lock) { lock.notifyAll(); }
return var;
}
@Override
public boolean stateFunction() { //Our condition for this lock
// if any variable is true: if(var_a || var_b || var_c || ...)
for(Variable var : vars) {
if(var.value() == true) return true;
}
return false;
}
}
//increments a value with delay, the condition is met when the provided count is reached
private static class SpinningWarrior implements Runnable, Condition {
private final int count;
private final long delay;
private final Variable var;
private int tick = 0;
public SpinningWarrior(OrNexusLock lock, int count, long delay) {
this.var = lock.newVar(this);
this.count = count; //What to count to?
this.delay = delay;
}
@Override
public void run() {
while (state_value==false) { //We're still counting up!
tick++;
chkState();
try {
Thread.sleep(delay);
} catch (InterruptedException ignored) {
break;
}
}
}
/**
* Though redundant value-change-notification are OK,
* its best to prevent them. As such its made clear to
* that we will ever change state once.
*/
private boolean state_value = false;
private void chkState() {
if(state_value ==true) return;
if(tick >= count) {
state_value = true;
var.valueChanged(); //Our value has changed
}
}
@Override
public boolean value() {
return state_value; //We could compute our condition in here, but for example sake.
}
}
}
欢迎,@贾米。我确信这包含了一个很好的答案,但它不是StackExchange的答案。我建议你提取描述作为你的答案,并根据需要用代码片段来说明它,而不是要求读者从你的代码转储中挖出相关的位。顺便说一句,我假设你有权在这里发布此代码 - 是否正确?我只是问,因为它看起来像一个课程或课程材料批发的类。 – 2015-07-22 16:48:41
谢谢,是的,我做到了!这是我的第一篇文章,它整合了大约一个小时的纯焦点(和一些哲学)。我几乎考虑为它制作一个图书馆,但它要密集。我相信它值得解释 - 简单来说 - 在JLS中似乎缺少组件的背后的天才。我会尽量压缩它! – 2015-07-22 16:56:27
看来,你的情况是从两个不同的源等待“通知”。您可能不需要在这两个对象本身上“等待”(正如在java synchronized(object) object.wait()
中那样),而是让它们都与队列对话或不与其他对象(如其他答案所述,阻止像LinkedBlockingQueue这样的集合)进行对话。
如果你真的想在两个不同的java对象上“等待”,你可以通过应用这个答案的一些原则来做到这一点:https://stackoverflow.com/a/31885029/32453(基本上新建一个线程,每个线程在每个等待的对象,让他们在通知对象本身时通知主线程),但管理同步的方面可能并不容易。
为了从处理任何线程的终止一个给定的设定,而无需等待所有这些完成,专用公共对象(下面lastExited
)可以被(在块wait()
和notify()
)用作监视器。需要更多监视器来确保在任何时候至多有一个线程正在退出(notifyExitMutex
),并且至多有一个线程正在等待任何线程退出(waitAnyExitMonitor
);因此对总是与不同的块相关。
例(全部过程终止按以下顺序处理线程完成):
import java.util.Random;
public class ThreadMonitor {
private final Runnable[] lastExited = { null };
private final Object notifyExitMutex = new Object();
public void startThread(final Runnable runnable) {
(new Thread(new Runnable() { public void run() {
try { runnable.run(); } catch (Throwable t) { }
synchronized (notifyExitMutex) {
synchronized (lastExited) {
while (true) {
try {
if (lastExited[0] != null) lastExited.wait();
lastExited[0] = runnable;
lastExited.notify();
return;
}
catch (InterruptedException e) { }
}
}
}
}})).start();
}
private final Object waitAnyExitMutex = new Object();
public Runnable waitAnyExit() throws InterruptedException {
synchronized (waitAnyExitMutex) {
synchronized (lastExited) {
if (lastExited[0] == null) lastExited.wait();
Runnable runnable = lastExited[0];
lastExited[0] = null;
lastExited.notify();
return runnable;
}
}
}
private static Random random = new Random();
public static void main(String[] args) throws InterruptedException {
ThreadMonitor threadMonitor = new ThreadMonitor();
int threadCount = 0;
while (threadCount != 100) {
Runnable runnable = new Runnable() { public void run() {
try { Thread.sleep(1000 + random.nextInt(100)); }
catch (InterruptedException e) { }
}};
threadMonitor.startThread(runnable);
System.err.println(runnable + " started");
threadCount++;
}
while (threadCount != 0) {
Runnable runnable = threadMonitor.waitAnyExit();
System.err.println(runnable + " exited");
threadCount--;
}
}
}
也许你可以张贴一些源代码,包括部分,其中“等待”被调用,这两个部分在您通知应该发生。 – 2011-06-07 12:41:03
*线程如何等待多个对象*,队列。锁定对象,放在队列的一端并解锁。另一方面:从其他方面弹出它,获得锁,处理,释放锁。沿着这些线路的东西。 – bestsss 2011-06-07 13:49:09