将一个线程置于休眠状态,直到另一个线程中的条件得到解决

时间:2008-11-26 01:27:09

标签: java concurrency locking conditional-statements countdownlatch

以下是完成(我认为)同样事情的两个代码块。

我基本上是在尝试学习如何使用Java 1.5的并发来摆脱Thread.sleep(long)。第一个示例使用ReentrantLock,第二个示例使用CountDownLatch。我想要做的就是让一个线程进入休眠状态,直到在另一个线程中解决了一个条件。

ReentrantLock对我用来决定是否唤醒另一个线程的布尔值提供锁定,然后我使用条件和等待/信号来休眠另一个线程。据我所知,我需要使用锁的唯一原因是,如果多个线程需要对布尔值的写访问权。

CountDownLatch似乎提供与ReentrantLock相同的功能,但没有(不必要的?)锁。然而,感觉就像我通过初始化它只需要一次倒计时来劫持它的预期用途。我认为当多个线程要处理同一个任务时,应该使用它,而不是当多个线程在等待一个任务时。

所以,问题:

  1. 我是否在ReentrantLock代码中使用锁定“正确的东西”?如果我只在一个线程中写入布尔值,那么锁是否必要?只要我在唤醒任何其他线程之前重置布尔值我就不会导致问题,我可以吗?

  2. 是否有一个类似于CountDownLatch的类我可以使用它来避免锁定(假设我应该在这个例子中避免它们),这更适合这个任务?

  3. 还有其他方法可以改进我应该注意的代码吗?

  4. 示例一:

    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 个答案:

答案 0 :(得分:9)

两种方法大致相同,只有CountDownLatch只能释放一次。之后,所有await()次呼叫都会立即返回。因此,如果您正在使用可能会出现故障的服务,CyclicBarrier实际上可能更合适。

如果您的情况确实是一次性交易,那么FutureTask会更合适。您可以调用等待服务可用的get(),然后只要get()返回就可以使用该服务。

你提到CountDownLatch允许在不使用Locks的情况下等待。但是,CountDownLatch和ReentrantLock都是使用AbstractQueuedSynchronizer实现的。在引擎盖下,它们提供相同的同步和可见性语义。

答案 1 :(得分:2)

在我看来,锁定/条件变量方法对于此任务更好。这里有一个类似的例子: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)。但是,不使用Locks的问题在于,根据服务停机的时间长短,您将在(isServiceDown())旋转。通过使用条件等待你告诉操作系统睡眠你的线程,直到虚假的唤醒(在条件的java文档中谈到),或直到条件在另一个线程中发出信号。

答案 2 :(得分:-1)

FULL RE-ENTRANT LOCKED ASYNC TASK EXAMPLE:

使用CODE FLOW SCHEMA:

enter image description here