代码之家  ›  专栏  ›  技术社区  ›  pankajt

Java中的条件变量是什么?

  •  6
  • pankajt  · 技术社区  · 16 年前

    Q1。 Java中的CONDVAR是什么?如果我看到下面的代码,条件变量是否必须在' 获取() '和' 互斥 街区?

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

    我有三根线 神话传说 , 神话传说 , 神话传说 运行调用相同函数的 公共活动() 触发函数 MyWorkRePoT() 例如

    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。 必须有吗 条件.信号( 对于每一个 条件.timedwait(时间);

    5 回复  |  直到 10 年前
        1
  •  6
  •   Jared Russell    10 年前

    Q1) 最好的资源可能是 JavaDoc 用于条件类。条件变量是一种机制,允许您在允许方法继续进行之前测试特定条件是否为真。对于您的示例,有两个条件: notFull 和 notEmpty .

    示例中显示的Put方法等待 未满的 条件变为“真”,然后再尝试将元素添加到数组中,并且一旦插入完成,它将向 诺威蒂 唤醒等待从数组中删除元素的所有被阻止线程的条件。

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

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

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

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

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

    使用 sleep() 代替定时等待不是一个好主意,因为如上所述,您需要调用 await() 方法在同步区域内,以及 睡眠() 当 等待() 做。这意味着任何休眠线程仍将持有它们获得的锁,从而导致其他线程不必要地阻塞。

    Q4) 严格来说,不,你不需要打电话 signal() 但是,如果您使用的是定时等待,那么这样做意味着在超时结束之前,所有的等待都不会返回,这是最低效的。

        2
  •  3
  •   alphazero    16 年前

    Q1: 一 Condition 对象与锁(又称互斥体)对象相关联(并从中获取)。类的JavaDoc对于其用法和应用程序相当清楚。要在需要获得锁的情况下等待,最好在try/finally块(如您所拥有的)中这样做。一旦获取锁的线程等待该锁的条件,锁就会自动放弃。

    Q2: 使用定时等待是必要的,以确保您的程序的活力,在这种情况下,您正在等待永远不会发生。它绝对是一个更复杂的形式,如果你不检查你已经超时的事实,并采取行动来处理超时的情况,它是完全无用的。

    使用sleep是等待某件事情发生的一种可接受的形式,但是如果您已经在使用一个锁(“mutex”),并且拥有该锁的条件变量,那么不使用它是没有意义的。 use the time wait method of the condition :

    例如,在代码中,您只是在等待给定的时间段,但不检查条件是否发生或是否超时。(这是一个bug。)您应该做的是检查您的定时调用是否返回了真或假。(如果返回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();
        }
    }
    

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

    如果您所要做的就是确保commonActivity()一次只由一个线程执行,并且commonActivity()的某些部分不需要争用控制,并且您确实需要该工具在等待时超时,那么您可以 simply use a Semaphore . 请注意,Sempahore对于 timed waits .

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

    [最终编辑:]) 为了更正式地说明这一点,条件通常用于两个或多个线程在一个任务上协同工作并且需要在线程之间进行交接的场景。

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

    在过去,我们将使用wait()和notify(),但这不是一个非常健壮(或非常安全)的机制。锁和条件对象被精确地设计来解决这些缺点。

    (A) good online resource as a starting point )

    Buy and read this book .

        3
  •  0
  •   Andrzej Doyle    16 年前

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

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

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

    使用 while 而不是 if 也就是说,consumer方法will consumer方法只有在它确实要返回某些内容时才会退出该块。一般来说,任何由某个条件触发的睡眠、等待或阻塞调用,以及您期望条件发生变化的位置,都应该处于 虽然 块来检查每个循环的条件。

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

    Q2 . 定时等待通常是一个好的实践-超时允许您定期对环境执行健全性检查(例如,关闭类型标志已翻转),而非定时等待只能由中断停止。另一方面,如果等待只是一直阻塞直到条件成立,那么它没有什么区别,它每50毫秒唤醒一次,直到 notify() 两秒钟后发生,或者如果它在这两秒钟内一直阻塞。

    至于wait()和sleep()-前者通常更可取,因为它意味着一旦你能采取行动,你就会被唤醒。 Thread.sleep(500) 意味着这条线是 一定地 在接下来的500毫秒内不做任何事情,即使等待的事情在2毫秒后就准备好了。 obj.wait(500) 另一方面,它会被唤醒2毫秒进入睡眠状态,并可以继续处理。因为睡眠会在你的程序中引入无条件的延迟,所以它们通常是一种更笨拙的方式来做任何事情——它们只适用于你不等待任何特定条件,但是 事实上 希望在给定时间内睡眠(例如,每60秒触发一次清理线程)。如果您正在“睡眠”,因为您正在等待其他线程先执行某些操作,请使用 等待() (或其他同步技术,如 CountDownLatch 相反。

    第3季度 . 通过-看起来那里有很多样板文件,而且因为代码中没有任何注释 和 你还没有解释它应该做什么,以及它应该如何表现,我不会尝试用你写的东西来逆向工程。;-)

        4
  •  0
  •   elder_george    16 年前

    Q1。条件变量是 monitors facility 它有时用于线程同步。我不认识这种特殊的实现,但通常情况下,条件变量的使用必须在关键部分完成,因此 mutex.acquire 和 release 是必需的。

    Q2。 timedwait 在条件变量或超时时等待信号,然后请求获取关键部分。所以它不同于睡眠。

    Q3。我不确定,但我认为您可以在Java中使用内置监视器功能: synchronized 相互排斥和 wait 和 notify 而不是Cond Vars。因此,您将减少代码的依赖性。

        5
  •  0
  •   Tadeusz Kopec for Ukraine yespbs    16 年前

    Q1。 我想 documentation 描述得很好。是的,对 await 或 signal 您应该持有与条件关联的锁。
    Q2。 timedWait 不在 Condition API,它在 TimeUnit 应用程序编程接口。如果你使用 条件 希望等待使用超时 await(long time, TimeUnit unit) . 如果你知道如果超时发生该怎么做,那么拥有一个超时通常是一个好主意——没有人希望程序永久挂起。
    睡眠是无条件地等待,等待是等待事件。他们有不同的目的。 Q3。 我不知道这段代码应该做什么。如果你想周期性地执行一些动作,在每次迭代之间有一些休息,那么就使用睡眠来代替条件。
    Q4。 正如我在上面写的,条件没有 timedwait 方法,他们有 等待 方法。呼唤 等待 意味着你想等待一些事件发生。这假设有时这个事件 确实发生 有人发出信号。对吗?