Что такое переменная условия в Java? - PullRequest
6 голосов
/ 29 октября 2009

Q1. Что такое condVar в Java? Если я вижу приведенный ниже код, обязательно ли условная переменная должна находиться в пределах блоков ' 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();
  }
}

У меня есть три потока myThreadA , myThreadB , myThreadC , которые выполняют одну и ту же функцию 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. Использует время ожидания хорошей практики. Я мог бы достичь того же с помощью сна (). Если использование sleep () плохо, почему?

Q3. Есть ли лучший способ сделать вышеуказанные вещи?

Q4. Обязательно ли иметь условие. Сигнал () для каждого условие. Время ожидания (время);

Ответы [ 5 ]

6 голосов
/ 29 октября 2009

Q1) Лучшим ресурсом для этого, вероятно, является JavaDoc для класса Condition. Переменные условия - это механизм, который позволяет вам проверить, выполняется ли определенное условие, прежде чем продолжить выполнение вашего метода. В вашем примере есть два условия: notFull и notEmpty.

Метод put, показанный в вашем примере, ожидает, пока условие notFull станет истинным, прежде чем попытается добавить элемент в массив, и после завершения вставки он сигнализирует условию notEmpty, чтобы разбудить все потоки, заблокированные в ожидании удалить элемент из массива.

... обязательна ли условная переменная должны быть в пределах 'mutex.acquire ()' и Блок mutex.release ()?

Любые вызовы для изменения переменных условия должны быть внутри синхронизированной области - это может быть через встроенное ключевое слово synchronized или один из классов синхронизатора, предоставляемых пакетом java.util.concurrent, например Lock . Если вы не синхронизировали переменные условия, возможны два отрицательных результата:

  1. Пропущенный сигнал - это когда один поток проверяет условие и обнаруживает, что оно не выполняется, но перед тем, как заблокировать, поступает другой поток, выполняет некоторое действие, чтобы условие стало истинным, а затем сигнализирует всем потокам жду на условии. К сожалению, первый поток уже проверил условие и все равно заблокируется, даже если он действительно может продолжаться.

  2. Вторая проблема - это обычная проблема, когда несколько потоков пытаются одновременно изменить общее состояние. В вашем примере несколько потоков могут вызывать put() одновременно, все они затем проверяют условие и видят, что массив не заполнен, и пытаются вставить его, тем самым перезаписывая элементы в массиве.

Q2) Временные ожидания могут быть полезны для целей отладки, поскольку они позволяют регистрировать информацию в случае, если поток не проснулся с помощью сигнала.

Использование sleep() вместо таймерного ожидания НЕ является хорошей идеей, потому что, как упоминалось выше, вам нужно вызвать метод await() в синхронизированной области, и sleep() не освобождает удерживаемые блокировки, в то время как await() делает. Это означает, что любой спящий поток все еще будет удерживать полученные блокировку, что приводит к ненужной блокировке других потоков.

Q4) Технически, вам не нужно звонить signal(), если вы используете синхронизированное ожидание, однако, это означает, что все ожидания не вернутся, пока не истечет время ожидания, что неэффективно, если не сказать больше.

3 голосов
/ 29 октября 2009

1: Объект Condition связан (и получен из) объекта Lock (он же mutext). Javadoc для класса довольно ясно относительно его использования и применения. Чтобы дождаться выполнения условия, вы должны получить блокировку, и это хорошая практика кодирования, чтобы делать это в блоке try / finally (как у вас). Как только поток, получивший блокировку, ожидает условия для этой блокировки, блокировка освобождается (атомарно).

Q2: Использование таймерного ожидания необходимо для обеспечения жизнеспособности вашей программы в случае, если условие, которого вы ждете, никогда не наступит. Это определенно более сложная форма, и она совершенно бесполезна, если вы не проверяете факт истечения времени ожидания и не принимаете меры для обработки условия истечения времени ожидания.

Использование сна является приемлемой формой ожидания чего-то происходящего, но если вы уже используете Lock ("mutex") и имеете переменную условия для этой блокировки, не имеет смысла не использовать время метод ожидания условия :

Например, в вашем коде вы просто ожидаете определенный период, но НЕ проверяете, произошло ли условие или истекло время ожидания. (Это ошибка.) Вам следует проверить, вернул ли ваш синхронизированный вызов значение 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: [отредактировано] Фрагменты кода требуют более подробного контекста, чтобы быть понятными. Например, не совсем понятно, все ли условия в потоках одинаковы (но я предполагаю, что они есть).

Если все, что вы пытаетесь сделать, это убедиться, что commonActivity () выполняется только одним потоком за раз, AND, некоторые разделы commonActivity () НЕ требуют контроля за конкуренцией, AND, вам требуется, чтобы средство истекло в ожидании вы можете просто использовать семафор . Обратите внимание, что у sempahore есть свой собственный набор методов для временных ожиданий .

Если ВСЕ из commonActivity () критически важны, и AND, вы действительно не против ожидания (без тайм-аутов) просто сделайте commonActivity () синхронизированным методом.

[окончательное редактирование :)] Чтобы быть более формальным, условия обычно используются в сценариях, когда у вас есть два или более потоков, взаимодействующих в задаче, и вам требуется передача обслуживания между потоками.

Например, у вас есть сервер, который обрабатывает асинхронные ответы на запросы пользователей, и пользователь ожидает выполнения объекта Future. Состояние в этом случае идеальное. Будущая реализация ожидает условия, и сервер сообщает о его завершении.

В старые времена мы использовали wait () и notify (), но это был не очень надежный (или тривиально безопасный) механизм. Объекты Lock и Condition были разработаны именно для устранения этих недостатков.

(A хороший онлайн-ресурс в качестве отправной точки )

Купите и прочитайте эту книгу .

0 голосов
/ 29 октября 2009

Q1. Я думаю, документация дает довольно хорошее описание. И да, до await или signal вы должны удерживать блокировку, связанную с условием.
Q2. timedWait не в Condition API, а в TimeUnit API. Если вы используете Condition и хотите установить тайм-аут ожидания, используйте await(long time, TimeUnit unit). И вообще, наличие тайм-аута - хорошая идея - никто не хочет, чтобы программа зависала вечно, - если вы знаете, что делать, если тайм-аут наступает.
Сон - это ожидание безоговорочно, а ожидание - ожидание события. У них разные цели. Q3. Я не знаю, что этот код должен делать. Если вы хотите выполнять какое-то действие циклически, с некоторым перерывом между каждой итерацией, используйте sleep вместо условий.
Q4. Как я уже писал выше, у условий нет метода timedwait, у них есть метод await. А вызов await означает, что вы хотите ждать какого-то события. Это предполагает, что иногда это событие действительно происходит , и кто-то сигнализирует об этом. Верно?

0 голосов
/ 29 октября 2009

Q1. Переменные условия являются частью средства мониторинга , которое иногда используется для синхронизации потоков. Я не распознаю эту конкретную реализацию, но обычно использование условных переменных должно выполняться в критической секции, поэтому требуются mutex.acquire и release.

Q2. timedwait ожидает сигнала по условной переменной ИЛИ время ожидания, а затем запрашивает критическую секцию. Так что это отличается от сна.

Q3. Я не уверен, но я думаю, что вы можете использовать функциональность встроенных мониторов в Java: synchronized для взаимного исключения и wait и notify вместо cond vars. Таким образом вы уменьшите зависимости вашего кода.

0 голосов
/ 29 октября 2009

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 означает, что потребительский метод будет потреблять, что потребительский метод выйдет из этого блока, только если ему определенно есть что вернуть. Вообще говоря, любой неактивный или ожидающий или блокирующий вызов, вызванный условием и ожидающий изменения условия, должен находиться в блоке while, который проверяет это условие в каждом цикле.

В вашей ситуации вы уже делаете это с оболочкой while (count == array.length) вокруг notFull.await().

Q2 . Временное ожидание, как правило, является хорошей практикой - тайм-аут позволяет вам периодически выполнять проверку работоспособности вашей среды (например, если был выключен флаг типа выключения), тогда как несвоевременное ожидание может быть остановлено только прерыванием. С другой стороны, если ожидание все равно будет продолжать блокироваться до тех пор, пока условие не станет истинным, это не имеет большого значения, если оно просыпается каждые 50 мс (скажем), пока notify() не произойдет через 2 секунды, или если оно просто блокирует постоянно в течение этих 2 секунд.

Что касается wait () и sleep () - первое обычно предпочтительнее, так как оно означает, что вы просыпаетесь, как только вы можете действовать. Thread.sleep(500) означает, что этот поток определенно ничего не делает в течение следующих 500 мс, даже если ожидаемая вещь готова через 2 мс. obj.wait(500) с другой стороны, проснулся бы на 2 мс во сне и мог бы продолжить обработку. Поскольку сны приводят к безусловным задержкам в вашей программе, они, как правило, являются более грубым способом сделать что-либо - они подходят только тогда, когда вы не ожидаете каких-либо конкретных условий, но на самом деле хотите спать в течение определенного времени (например, поток очистки, который срабатывает каждые 60 секунд). Если вы «спите», потому что ожидаете, что какой-то другой поток сначала что-то сделает, используйте взамен wait() (или другой синхронный метод, такой как CountDownLatch).

Q3 . Проходите - похоже, там много шаблонов, и, поскольку в коде нет комментариев в и , вы не объяснили, что он должен делать и как он должен вести себя, я не собираюсь пробовать и перепроектировать это из того, что вы написали. ; -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...