Переведите один поток в спящий режим, пока условие не будет разрешено в другом потоке. - PullRequest
15 голосов
/ 26 ноября 2008

Вот два куска кода, которые выполняют (как мне кажется, одно и то же).

Я в основном пытаюсь научиться использовать параллелизм Java 1.5 для выхода из Thread.sleep (long). Первый пример использует ReentrantLock, а второй пример использует CountDownLatch. Суть того, что я пытаюсь сделать, это уложить один поток в спящий режим, пока условие не будет решено в другом потоке.

ReentrantLock обеспечивает блокировку для логического значения, которое я использую, чтобы решить, следует ли пробуждать другой поток или нет, а затем я использую Условие с ожиданием / сигналом для сна другого потока. Насколько я могу судить, единственная причина, по которой мне нужно было бы использовать блокировки, заключается в том, что доступ к записи логического значения требовался более чем одному потоку.

CountDownLatch, по-видимому, обеспечивает ту же функциональность, что и ReentrantLock, но без (ненужных?) Блокировок. Тем не менее, я чувствую, что я как бы угоняю его предполагаемое использование, инициализируя его только одним необходимым обратным отсчетом. Я думаю, что он должен использоваться, когда несколько потоков будут работать над одной и той же задачей, а не когда несколько потоков ожидают выполнения одной задачи.

Итак, вопросы:

  1. Использую ли я блокировки для «правильных вещей» в коде ReentrantLock? Если я пишу только логическое значение в одном потоке, нужны ли блокировки? До тех пор, пока я перезагружаю логическое значение перед пробуждением любых других потоков, я не могу вызвать проблемы, могу ли я?

  2. Существует ли класс, аналогичный CountDownLatch, который я могу использовать, чтобы избежать блокировок (в данном случае я должен избегать их), который более естественно подходит для этой задачи?

  3. Существуют ли другие способы улучшения этого кода, о которых мне следует знать?

ПРИМЕР ПЕРВЫЙ:

import java.util.concurrent.locks.*;

public class ReentrantLockExample extends Thread {

//boolean - Is the service down?
boolean serviceDown;

// I am using this lock to synchronize access to sDown
Lock serviceLock; 
// and this condition to sleep any threads waiting on the service.
Condition serviceCondition;

public static void main(String[] args) {
    Lock l = new ReentrantLock();
    Condition c = l.newCondition(); 
    ReentrantLockExample rle = new ReentrantLockExample(l, c);

    //Imagine this thread figures out the service is down
    l.lock();
    try {
        rle.serviceDown = true;
    } finally {
        l.unlock();
    }

    int waitTime = (int) (Math.random() * 5000);
    System.out.println("From main: wait time is " + waitTime);
    rle.start();
    try {
        //Symbolizes some random time that the service takes to come back up.
        Thread.sleep(waitTime);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //Imagine this thread figures out that the service is back up.
    l.lock();
    try {
        rle.serviceDown = false;
        c.signal();
    } finally {
        l.unlock();
    }

}

//Constructor
public ReentrantLockExample(Lock l, Condition c) {  
    this.serviceLock = l;
    this.serviceCondition = c; 
}

/*
 * Should wait for this imaginary service to come back online.
 */
public void run() {
    System.out.println("Thread: start awaiting");
    serviceLock.lock();
    try {
        while (isServiceDown())
        {           
            serviceCondition.await();
        }
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        serviceLock.unlock();
    }
    System.out.println("Thread: done awaiting");
}


private boolean isServiceDown() {       
    return serviceDown;
}
}

ПРИМЕР ВТОРОЙ:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;

public class CountDownLatchExample extends Thread {

    //boolean - Is the service down?
    boolean serviceDown;

    // I am using this latch to wait on the service.
    CountDownLatch serviceLatch; 


    public static void main(String[] args) {
        CountDownLatch cdl = new CountDownLatch(1);     
        CountDownLatchExample cdle = new CountDownLatchExample(cdl);

        //Service goes down.
        cdle.serviceDown = true;        

        int waitTime = (int) (Math.random() * 5000);
        System.out.println("From main: wait time is " + waitTime);
        cdle.start();
        try {
            //Symbolizes some random time that the service takes to come back up.
            Thread.sleep(waitTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Service comes back up.
        cdle.serviceDown = false;
        cdl.countDown();    
    }

    //Constructor 
    public CountDownLatchExample(CountDownLatch cdl) {  
        this.serviceLatch = cdl;         
    }

    /*
     * Should wait for this imaginary service to come back online.
     */
    public void run() {
        System.out.println("Thread: start awaiting");
        try {
            while (isServiceDown()) {           
                serviceLatch.await();
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Thread: done awaiting");
    }

    private boolean isServiceDown() {       
        return serviceDown;
    }
}

Ответы [ 3 ]

9 голосов
/ 26 ноября 2008

Любой подход приблизительно эквивалентен, за исключением того, что CountDownLatch может быть выпущен только один раз. После этого все await () вызовы возвращаются мгновенно. Так что CyclicBarrier может на самом деле быть более подходящим, если вы работаете со службой, которая может идти вниз и вверх.

Если ваше состояние действительно одноразовое, тогда более подходящим будет FutureTask . Вы можете вызвать get () , который будет ждать, пока служба станет доступной, и затем вы можете использовать службу, как только get () вернется.

Вы упоминаете, что CountDownLatch позволяет ждать без использования Locks. Однако и CountDownLatch, и ReentrantLock реализованы с использованием AbstractQueuedSynchronizer . Под капотом они обеспечивают идентичную семантику синхронизации и видимости.

2 голосов
/ 26 ноября 2008

На мой взгляд, для этой задачи лучше подходит метод блокировки / условия. Вот пример, подобный вашему: http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

В ответ на защиту логического значения. Вы можете использовать volatile (http://www.ibm.com/developerworks/java/library/j-jtp06197.html).. Однако проблема, связанная с неиспользованием блокировок, заключается в том, что в зависимости от того, как долго работает ваш сервис, вы будете крутить время while (isServiceDown ()). Используя условие wait, вы сообщаете ОС спать в вашей ветке до ложного пробуждения (об этом говорится в документации по Java для Условий) или до тех пор, пока условие не будет передано в другой ветке.

0 голосов
/ 29 июня 2015
...