java 使一个线程休眠,直到另一个线程中的条件得到解决

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/319485/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 11:56:32  来源:igfitidea点击:

Put one thread to sleep until a condition is resolved in another thread

javaconcurrencylockingconditional-statementscountdownlatch

提问by Nathan Spears

Here are two chunks of code that accomplish (what I think is) the same thing.

这是完成(我认为是)同一件事的两段代码。

I basically am trying to learn how to use Java 1.5's concurrency to get away from Thread.sleep(long). The first example uses ReentrantLock, and the second example uses CountDownLatch. The jist of what I am trying to do is put one thread to sleep until a condition is resolved in another thread.

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

The ReentrantLock provides a lock on the boolean I'm using to decide whether to wake the other thread or not, and then I use the Condition with await/signal to sleep the other thread. As far as I can tell, the only reason I would need to use locks is if more than one thread required write access to the boolean.

ReentrantLock 为我用来决定是否唤醒另一个线程的布尔值提供了一个锁,然后我使用带有 await/signal 的 Condition 来休眠另一个线程。据我所知,我需要使用锁的唯一原因是如果多个线程需要对布尔值进行写访问。

The CountDownLatch seems to provide the same functionality as the ReentrantLock but without the (unnecessary?) locks. However, it feels like I am kind of hiHymaning its intended use by initializing it with only one countdown necessary. I think it's supposed to be used when multiple threads are going to be working on the same task, not when multiple threads are waiting on one task.

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

So, questions:

所以,问题:

  1. Am I using locks for "the right thing" in the ReentrantLock code? If I am only writing to the boolean in one thread, are the locks necessary? As long as I reset the boolean before waking up any other threads I can't cause a problem, can I?

  2. Is there a class similar to CountDownLatch I can use to avoid locks (assuming I should be avoiding them in this instance) that is more naturally suited to this task?

  3. Are there any other ways to improve this code I should be aware of?

  1. 我是否在 ReentrantLock 代码中为“正确的事情”使用了锁?如果我只在一个线程中写入布尔值,是否需要锁定?只要我在唤醒任何其他线程之前重置布尔值,我就不会引起问题,可以吗?

  2. 是否有一个类似于 CountDownLatch 的类可以用来避免锁(假设我应该在这种情况下避免它们)更自然地适合此任务?

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

EXAMPLE ONE:

示例一:

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

EXAMPLE TWO:

示例二:

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

采纳答案by Craig P. Motlin

Either approach is roughly equivalent, except that a CountDownLatchcan only be released once. After that all await()calls return instantly. So a CyclicBarriermay actually be more appropriate if you are working with a service that may go down and up.

这两种方法大致等效,只是CountDownLatch只能释放一次。之后,所有await()调用都会立即返回。因此,如果您使用的服务可能会下降和上升,那么CyclicBarrier实际上可能更合适。

If your condition really is a one-shot deal, then a FutureTaskwould be more appropriate. You could call get()which would wait for the service to become available, and then you could use the service as soon as get() returns.

如果您的条件确实是一次性交易,那么FutureTask会更合适。您可以调用get()等待服务变为可用,然后您可以在 get() 返回后立即使用该服务。

You mention that CountDownLatch allows waiting without using Locks. However, both CountDownLatch and ReentrantLockare implemented using AbstractQueuedSynchronizer. Under the hood, they provide identical synchronization and visibility semantics.

您提到 CountDownLatch 允许在不使用锁的情况下进行等待。但是,CountDownLatch 和ReentrantLock都是使用AbstractQueuedSynchronizer实现的。在幕后,它们提供相同的同步和可见性语义。

回答by grepsedawk

The lock/condition variable approach is better for this task in my opinion. There is a similar example to yours here: http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

在我看来,锁定/条件变量方法更适合这项任务。这里有一个类似的例子:http: //java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

In response to protecting a boolean. You could use volatile(http://www.ibm.com/developerworks/java/library/j-jtp06197.html). However the problem with not using Locks is that depending on how long your service is down for you will be spinning on while(isServiceDown()). By using a condition wait you tell the OS to sleep your thread until a spurious wake up (talked about in the java docs for Condition), or until the condition is signaled in the other thread.

响应保护布尔值。您可以使用 volatile(http://www.ibm.com/developerworks/java/library/j-jtp06197.html)。然而,不使用锁的问题在于,根据您的服务关闭的时间长短,您将在 while(isServiceDown()) 上旋转。通过使用条件等待,您可以告诉操作系统让您的线程休眠,直到虚假唤醒(在条件的 java 文档中谈到),或者直到条件在另一个线程中发出信号。

回答by ceph3us