什么是java中的条件变量?

Q1。 什么是Java中的condVar? 如果我看到下面的代码,条件变量是否必须在’ mutex.acquire() ‘和’ mutex.release() ‘块中?

public void put(Object x) throws InterruptedException { mutex.acquire(); try { while (count == array.length) notFull.await(); array[putPtr] = x; putPtr = (putPtr + 1) % array.length; ++count; notEmpty.signal(); } finally { mutex.release(); } } 

我有三个线程myThreadAmyThreadBmyThreadC运行调用相同的函数commonActivity() ,它触发函数myWorkReport(),例如

 public void myWorkReport(){ mutexMyWork.acquire(); try{ while(runMyWork){ doWork(); conditionMyWork.timedwait(sleepMyWork); } } finally{ mutexMyWork.release() } } public void commonActivity(){ try{ conditionMyWork.signal(); }finally{ //cleanup } } public void myThreadA(){ mutexA.acquire(); try{ while(runningA){ //runningA is a boolean variable, this is always true as long as application is running conditionA.timedwait(sleepA); commonActivity(); } } finally{ mutexA.release(); } } public void myThreadB(){ mutexB.acquire(); try{ while(runningB){ //runningB is a boolean variable, this is always true as long as application is running conditionB.timedwait(sleepB); commonActivity(); } } finally{ mutexB.release(); } } public void myThreadC(){ mutexC.acquire(); try{ while(runningC){ //runningC is a boolean variable, this is always true as long as application is running. conditionC.timedwait(sleepC); commonActivity(); } } finally{ mutexC.release(); } } 

Q2。 使用timedwait是一个很好的做法。 我可以通过使用sleep()来实现相同的目标。 如果使用sleep()调用很糟糕,为什么?

Q3。 有没有更好的方法来做上述事情?

Q4。 是否必须为每个condition.timedwait(time)设置condition.signal();

Q1)最好的资源可能是Condition类的JavaDoc 。 条件变量是一种机制,允许您在允许方法继续之前测试特定条件是否成立。 在您的示例中,有两个条件, notFullnotFull

您的示例中显示的put方法在尝试将元素添加到数组之前等待notFull条件变为true,并且一旦插入完成,它会发出notEmpty条件以唤醒任何阻塞的线程,等待从数组中删除元素。

…条件变量是否必须在’mutex.acquire()’和’mutex.release()’块中?

任何更改条件变量的调用都需要在同步区域内 – 这可以通过内置的synchronized关键字或java.util.concurrent包提供的同步类之一,如Lock 。 如果您没有同步条件变量,则有两种可能的负面结果:

  1. 一个错过的信号 – 这是一个线程检查条件并发现它不成立的地方,但是在它阻塞另一个线程进入之前,执行一些操作以使条件变为真,然后发出信号等待条件的所有线程。 不幸的是,第一个线程已经检查了条件,并且无论如何都会阻塞,即使它实际上可以继续。

  2. 第二个问题是通常的问题,您可以让多个线程同时尝试修改共享状态。 在你的例子中,多个线程可以同时调用put() ,然后所有线程都检查条件并看到数组未满并尝试插入其中,从而覆盖数组中的元素。

Q2)定时等待对于调试目的非常有用,因为它们允许您在线程未通过信号唤醒的情况下记录信息。

使用sleep()代替定时等待并不是一个好主意,因为如上所述,你需要在同步区域内调用await()方法,而sleep()不会释放任何持有的锁,而await()会。 这意味着任何hibernate线程仍将保持它们已获取的锁定,从而导致其他线程不必要地阻塞。

Q4)从技术上讲,如果您正在使用定时等待,则不需要调用signal() ,但这样做意味着所有等待都不会返回,直到超时结束,这至少是低效率。

Q1: Condition对象与Lock(也称为mutext)对象相关联(并从中获取)。 该类的javadoc在使用和应用方面相当清楚。 要等待你需要获得锁定的条件,并且在try / finally块中这样做是很好的编码实践(就像你一样)。 一旦获取锁的线程等待该锁的条件,锁就会被放弃(primefaces地)。

Q2:在您等待的条件永远不会发生的情况下,使用定时等待是必要的,以确保程序的活跃性。 它绝对是一种更复杂的forms,如果你没有检查你已经超时并采取行动来处理超时条件,这完全没用。

使用sleep是等待某些事情发生的可接受forms,但是如果你已经使用了Lock(“mutex”)并且为该锁定了一个条件变量,那么不使用条件的time wait方法是没有意义的 :

例如,在您的代码中,您只是等待一段时间,但是您不会检查条件是否发生或者您是否超时。 (这是一个错误。)你应该做的是检查你的定时呼叫是真还是假。 (如果它返回false,则它超时并且条件尚未发生(尚未))。

 public void myThreadA(){ mutexA.acquire(); try{ while(runningA){ //runningA is a boolean variable if(conditionA.await (sleepATimeoutNanos)) commonActivity(); else { // timeout! anything sensible to do in that case? Put it here ... } } } finally{ mutexA.release(); } } 

Q3:[编辑]代码片段需要更详细的上下文才能理解。 例如,如果线程中的条件都是相同的(但我假设它们是),则不完全清楚。

如果您要做的就是确保commonActivity()一次仅由一个线程执行,并且,commonActivity()的某些部分不需要争用控制,并且您确实需要设施在等待时间超时那么,你可以简单地使用一个信号量 。 请注意,sempahore有自己的一组定时等待方法。

如果所有的commonActivity()都很关键,那么,你真的不介意等待(没有超时)只需使commonActivity()成为同步方法。

[final edit :)]为了更正式一点,条件通常用于你有两个或多个线程合作任务的场景,你需要在线程之间进行手动操作。

例如,您有一台服务器正在处理对用户请求的异步响应,并且用户正在等待Future对象的实现。 在这种情况下,条件是完美的。 未来的实施正在等待条件,服务器发出信号表示完成。

在过去,我们将使用wait()和notify(),但这不是一个非常强大(或简单安全)的机制。 Lock和Condition对象的设计正是为了解决这些缺点。

(一个很好的在线资源作为起点 )

购买并阅读本书 。

Q1 。 我相信“条件变量”,你指的是你检查的东西,以确定你等待的条件。 例如 – 如果您有典型的生产者 – 消费者情况,您可以将其实现为:

 List list; public T get() { synchronized (list) { if (list.get(0) == null) { list.wait(); } return list.get(0); } } public void put(T obj) { synchronized (list) { list.add(obj); list.notify(); } } 

但是,由于虚假线程唤醒的可能性,当列表仍为空时,消费者方法可能会退出wait()调用。 因此,最好使用条件变量来等待/睡眠等。 直到条件为真:

 while (list.get(0) == null) { list.wait(); } 

使用while不是if意味着使用者方法将消费者方法只有当它肯定有东西要返回时才会退出该块。 一般来说,由条件触发的任何睡眠或等待或阻塞调用,以及您希望条件发生变化的位置,都应该在一个while块中检查每个循环的条件。

在你的情况下,你已经使用notFull.await()while (count == array.length)包装器notFull.await()

Q2 。 定时等待通常是一种很好的做法 – 超时允许您定期对您的环境执行完整性检查(例如,已经关闭了关闭类型标志),而非定时等待只能通过中断来停止。 另一方面,如果等待将继续阻塞直到条件为真,那么它每隔50毫秒(比如说)唤醒直到2秒后notify()发生,或者它只是阻塞它几乎没有什么区别不断为那2秒。

至于wait()vs sleep() – 前者通常更可取,因为这意味着一旦你能够采取行动你就会被唤醒。 Thread.sleep(500)意味着这个线程绝对不会在接下来的500ms内做任何事情,即使它等待的东西在2ms之后就准备好了。 obj.wait(500)在睡眠中被唤醒2ms并且可以继续处理。 由于睡眠会在您的程序中引入无条件延迟,因此它们通常是一种笨拙的方式 – 它们只适用于您没有等待任何特定条件但实际上想要睡眠一段时间(例如清理线程)每60秒发射一次)。 如果你正在“hibernate”,因为你正在等待其他一些线程先做某事,那么请使用wait() (或其他同步技术,如CountDownLatch )。

Q3 。 通过 – 看起来那里有很多样板,而且由于代码中没有任何注释你没有解释它应该做什么以及它的表现如何,我不会试图反转 – 从你写的东西开始。 😉

Q1。 条件变量是监视工具的一部分,有时用于线程同步。 我不认识这个特定的实现,但通常条件变量的使用必须在临界区完成,因此需要mutex.acquirerelease

Q2。 timedwait等待条件变量上的信号或超时,然后需要关键部分。 所以它与睡眠不同。

Q3。 我不确定,但我认为您可以在java中使用内置监视器function: synchronized用于互斥并waitnotify而不是cond vars。 因此,您将减少代码的依赖性。

Q1。 我认为文档提供了很好的描述。 是的, await或发signal你应该保持与条件相关的锁。
Q2。 timedWait不在Condition API中,它在TimeUnit API中。 如果您使用Condition并希望等待使用await(long time, TimeUnit unit)超时await(long time, TimeUnit unit) 。 超时通常是一个好主意 – 没有人希望程序永远挂起 – 只要你知道如果发生超时该怎么办。
睡觉是无条件等待,等待等待事件。 他们有不同的目的。 Q3。 我不知道这段代码应该做什么。 如果要循环执行某些操作,在每次迭代之间有一些中断,请使用sleep而不是条件。
Q4。 正如我上面写的那样条件没有timedwait方法,它们有await方法。 呼叫await意味着你想要等待一些事件发生。 这假设有时候这个事件确实发生了 ,有人发出了这个信号。 对?