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

StackOverflow https://stackoverflow.com/questions/319485

Вопрос

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

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

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

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

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

<Ол>
  • Использую ли я блокировки для " правильной вещи " в коде ReentrantLock? Если я пишу только логическое значение в одном потоке, нужны ли блокировки? До тех пор, пока я перезагружаю логическое значение до пробуждения любых других потоков, я не могу вызвать проблемы, могу ли я?

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

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

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

    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;
        }
    }
    
    Это было полезно?

    Решение

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

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

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

    Другие советы

    На мой взгляд, для этой задачи лучше подходит метод блокировки / условия. Вот пример, похожий на ваш: 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 ()). Используя условное ожидание, вы сообщаете операционной системе о том, что ваш поток должен оставаться в спячке до ложного пробуждения (о котором говорится в документации по Java для Условий) или до тех пор, пока условие не будет сообщено в другом потоке.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top