java中的条件变量是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1643141/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 18:36:52  来源:igfitidea点击:

What is a Condition Variable in java?

javamultithreading

提问by pankajt

Q1.What is a condVar in Java? If I see the code below, does a condition variable necessarily have to be within the 'mutex.acquire()' and 'mutex.release()' block?

一季度。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();
  }
}

I have three threads myThreadA, myThreadB, myThreadCrunning which call the same function commonActivity()which triggers the function myWorkReport()e.g.

我有三个线程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.Is using timedwait a good practice. I could have achieved the same by using sleep(). If using sleep() call is bad, Why?

Q2。使用 timedwait 是一种很好的做法。我可以通过使用 sleep() 来达到同样的效果。如果使用 sleep() 调用不好,为什么?

Q3.Is there any better way to do the above stuff?

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

Q4.Is it mandatory to have condition.signal()for every condition.timedwait(time);

第 4 季度。是否必须有condition.signal()为每condition.timedwait(时间);

采纳答案by Jared Russell

Q1)The best resource for this is probably the JavaDocfor the Condition class. Condition variables are a mechanism that allow you to test that a particular condition holds true before allowing your method to proceed. In the case of your example there are two conditions, notFulland notEmpty.

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

The put method shown in your example waits for the notFullcondition to become true before it attempts to add an element into the array, and once the insertion completes it signals the notEmptycondition to wake up any threads blocked waiting to remove an element from the array.

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

...does a condition variable necessarily have to be within the 'mutex.acquire()' and 'mutex.release()' block?

...条件变量是否必须位于“mutex.acquire()”和“mutex.release()”块内?

Any calls to change the condition variables do need to be within a synchronized region - this can be through the built in synchronizedkeyword or one of the synchronizer classes provided by the java.util.concurrentpackage such as Lock. If you did not synchronize the condition variables there are two possible negative outcomes:

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

  1. A missed signal - this is where one thread checks a condition and finds it does not hold, but before it blocks another thread comes in, performs some action to cause the condition to become true, and then signals all threads waiting on the condition. Unfortunately the first thread has already checked the condition and will block anyway even though it could actually proceed.

  2. The second issue is the usual problem where you can have multiple threads attempting to modify the shared state simultaneously. In the case of your example multiple threads may call put()simultaneously, all of them then check the condition and see that the array is not full and attempt to insert into it, thereby overwriting elements in the array.

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

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

Q2)Timed waits can be useful for debugging purposes as they allow you to log information in the event the thread is not woken up via a signal.

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

Using sleep()in place of a timed wait is NOT a good idea, because as mentioned above you need to call the await()method within a synchronized region, and sleep()does not release any held locks, while await()does. This means that any sleeping thread will still hold the lock(s) they have acquired, causing other threads to block unnecessarily.

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

Q4)Technically, no you don't need to call signal()if you're using a timed wait, however, doing so means that all waits will not return until the timeout has elapsed, which is inefficient to say the least.

Q4)从技术上讲,不,signal()如果您使用定时等待,则不需要调用,但是,这样做意味着所有等待都不会在超时过去之前返回,这至少可以说是低效的。

回答by Andrzej Doyle

Q1. I believe by "condition variable", you're referring to something you check to determine the condition that you waited on. For example - if you have the typical producer-consumer situation, you might implement it as something like:

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

List<T> 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();
    }
}

However, due to the potential of spurious thread wakeups, it is possible for the consumer method to come out of the wait()call while the list is still empty. Thus it's good practice to use a condition variable to wait/sleep/etc. until the condition is true:

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

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

using whileinstead of ifmeans that the consumer method will consumer method will only exit that block if it definitely has something to return. Broadly speaking, any sleep or wait or blocking call that was triggered by a condition, and where you expect the condition to change, should be in a whileblock that checks that condition every loop.

使用while代替if意味着消费者方法将消费者方法只有在它肯定有东西要返回时才会退出该块。从广义上讲,任何由条件触发的睡眠或等待或阻塞调用,以及您期望条件改变的地方,都应该在一个while块中,在每个循环中检查该条件。

In your situation you're already doing this with the while (count == array.length)wrapper around notFull.await().

在你的情况你已经用这样while (count == array.length)来包裹notFull.await()

Q2. Timed wait is generally a good practice - the timeout allows you to periodically perform a sanity check on your environment (e.g. has a shutdown-type flag been flipped), whereas a non-timed wait can only be stopped by interruption. On the other hand, if the wait is going to just keep blocking anyway until the condition is true, it makes little difference it it wakes up every 50 ms (say) until the notify()happens 2 seconds later, or if it just blocks constantly for those 2 seconds.

Q2。定时等待通常是一个很好的做法 - 超时允许您定期对您的环境执行健全性检查(例如翻转关闭类型标志),而非定时等待只能通过中断停止。另一方面,如果等待将一直阻塞直到条件为真,那么它每 50 毫秒唤醒一次(比如)直到notify()2 秒后发生,或者如果它只是不断地阻塞那些2 秒。

As for wait() vs sleep() - the former is generally preferable, since it means you get woken up as soon as you are able to take action. Thread.sleep(500)means that this thread is definitelynot doing anything for the next 500ms, even if the thing it's waiting for is ready 2ms later. obj.wait(500)on the other hand would have been woken up 2ms into its sleep and can continue processing. Since sleeps introduce unconditional delays in your program, they're generally a clunkier way to do anything - they're only suitable when you're not waiting on any specific condition but actuallywant to sleep for a given time (e.g. a cleanup thread that fires every 60 seconds). If you're "sleeping" because you're waiting for some other thread to do something first, use a wait()(or other synchronous technique such as a CountDownLatch) instead.

至于 wait() 与 sleep() - 前者通常更可取,因为这意味着一旦您能够采取行动,您就会被唤醒。 Thread.sleep(500)意味着该线程在接下来的 500 毫秒内绝对不会做任何事情,即使它等待的事情在 2 毫秒后准备就绪。 obj.wait(500)另一方面,它会被唤醒 2 毫秒进入睡眠状态并可以继续处理。由于睡眠会在您的程序中引入无条件延迟,因此它们通常是一种更笨拙的做任何事情的方式 - 它们仅适用于您不等待任何特定条件但实际上想要睡眠给定时间(例如每 60 秒触发一次)。如果你因为等待其他线程先做某事而“睡觉”,请使用wait()(或其他同步技术,例如 a CountDownLatch)。

Q3. Pass - it looks like there's a lot of boilerplate there, and since the code doesn't have any comments in andyou haven't explained what it's supposed to do and how it's meant to behave, I'm not going to try and reverse-engineer that from what you're written. ;-)

Q3。通过 - 看起来那里有很多样板,而且由于代码中没有任何注释,而且您还没有解释它应该做什么以及它的行为方式,我不会尝试反向-从你写的东西中设计出来。;-)

回答by elder_george

Q1. Condition variables are part of monitors facilitywhich is sometimes used for threads synchronization. I don't recognize this particular implementations but usually conditional variables usage must be done in the critical section, thus mutex.acquireand releaseare required.

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

Q2. timedwaitwaits for signal on condition variable OR time out and then reqcquires critical section. So it differs from sleep.

Q2。timedwait等待条件变量 OR 超时的信号,然后请求临界区。所以它与睡眠不同。

Q3. I am not sure, but I think you may use built-in monitors functionality in java: synchronizedfor mutual exclusion and waitand notifyinstead of cond vars. Thus you will reduce dependencies of your code.

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

回答by alphazero

Q1: A Conditionobject is associated (and acquired from) a Lock (aka mutext) object. The javadoc for the class is fairly clear as to its usage and application. To wait on the condition you need to have acquired the lock, and it is good coding practice to do so in a try/finally block (as you have). As soon as the thread that has acquired the lock waits on a condition for that lock, the lock is relinquished (atomically).

Q1:一个Condition对象关联(并从)一个 Lock(又名 mutext)对象。该类的 javadoc 对其用法和应用非常清楚。要等待您需要获得锁的条件,在 try/finally 块中这样做是一种很好的编码习惯(就像您一样)。一旦获得锁的线程等待该锁的条件,锁就会被释放(以原子方式)。

Q2: Using timed wait is necessary to insure liveness of your program in case where the condition you are waiting for never occurs. Its definitely a more sophisticated form, and it is entirely useless if you do not check for the fact that you have timed out and take action to handle the time out condition.

Q2:在您等待的情况永远不会发生的情况下,使用定时等待对于确保程序的活跃性是必要的。它绝对是一种更复杂的形式,如果您不检查已超时的事实并采取措施处理超时情况,则它完全没有用。

Using sleep is an acceptable form of waiting for something to occur, but if you are already using a Lock ("mutex") and have a condition variable for that lock, it make NO sense not to use the time wait method of the condition:

使用 sleep 是一种可以接受的等待某事发生的形式,但是如果您已经在使用锁(“互斥锁”)并且有该锁的条件变量,那么不使用条件的时间等待方法是没有意义

For example, in your code, you are simply waiting for a given period but you do NOT check to see if condition occurred or if you timed out. (That's a bug.) What you should be doing is checking to see if your timed call returned true or false. (If it returns false, then it timed out & the condition has NOT occured (yet)).

例如,在您的代码中,您只是在等待给定的时间,但您不会检查条件是否发生或是否超时。(这是一个错误。)您应该做的是检查您的定时调用是否返回 true 或 false。(如果它返回 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: [edited] The code fragments require a more detailed context to be comprehensible. For example, its not entirely clear if the conditions in the threads are all the same (but am assuming that they are).

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

If all you are trying to do is insure commonActivity() is executed only by one thread at a time, AND, certain sections of the commonActivity() do NOT require contention control, AND, you do require the facility to time out on your waits, then, you can simply use a Semaphore. Note that sempahore has its own set of methods for timed waits.

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

If ALL of the commonActivity() is critical, AND, you really don't mind waiting (without timeouts) simply make commonActivity() a synchronized method.

如果所有的 commonActivity() 都是关键的,并且,您真的不介意等待(没有超时),只需将 commonActivity() 设为同步方法即可。

[final edit:)] To be more formal about it, conditions are typically used in scenarios where you have two or more thread co-operating on a task and you require hand offs between the threads.

[最终编辑:)] 更正式地说,条件通常用于有两个或多个线程协作处理任务并且需要线程之间切换的场景。

For example, you have a server that is processing asynchronous responses to user requests and the user is waiting for fulfillment of a Future object. A condition is perfect in this case. The future implementation is waiting for the condition and the server signals its completion.

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

In the old days, we would use wait() and notify(), but that was not a very robust (or trivially safe) mechanism. The Lock and Condition objects were designed precisely to address these shortcomings.

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

(A good online resource as a starting point)

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

Buy and read this book.

购买并阅读这本书

回答by Tadeusz Kopec

Q1.I think documentationgives quite good description. And yes, to awaitor signalyou should hold the lock associated with the condition.
Q2.timedWaitis not in ConditionAPI, it's in TimeUnitAPI. If you use Conditionand want to have a timeout for waiting use await(long time, TimeUnit unit). And having a timeout is generally a good idea - nobody wants a program to hang forever - provided you know what to do if timeout occurs.
Sleep is for waiting unconditionally and await is for waiting for an event. They have different purposes. Q3.I don't know what this code is expected to do. If you want to perform some action cyclically, with some break between each iteration, use sleep instead of conditions.
Q4.As I wrote above conditions don't have timedwaitmethod, they have awaitmethod. And calling awaitmeans you want to wait for some event to happen. This assumes that sometimes this event does happenand someone signals this. Right?

一季度。我认为文档给出了很好的描述。是的,await或者signal你应该持有与条件相关的锁。
Q2。timedWait不在ConditionAPI 中,它在TimeUnitAPI 中。如果您使用Condition并希望等待使用超时await(long time, TimeUnit unit)。设置超时通常是一个好主意——没有人希望程序永远挂起——前提是你知道如果发生超时该怎么办。
Sleep 用于无条件等待,await 用于等待事件。他们有不同的目的。 Q3。我不知道这段代码应该做什么。如果您想循环执行某些操作,在每次迭代之间有一些休息时间,请使用睡眠而不是条件。
第 4 季度。正如我上面写的,条件没有timedwait方法,它们有await方法。调用await意味着您要等待某个事件发生。这假设有时此事件确实发生并且有人发出信号。对?