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

标签 java concurrency locking conditional-statements countdownlatch

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

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

ReentrantLock 为我用来决定是否唤醒另一个线程的 boolean 值提供了一个锁,然后我使用带有 await/signal 的条件让另一个线程 hibernate 。据我所知,我需要使用锁的唯一原因是是否有多个线程需要对 boolean 值的写访问权。

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

那么,问题:

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

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

最佳答案

这两种方法大致相同,除了 CountDownLatch只能发布一次。之后所有 await()调用立即返回。所以一个CyclicBarrier如果您正在使用可能会下降和上升的服务,实际上可能更合适。

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

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

关于java - 将一个线程置于 sleep 状态,直到另一个线程中的条件得到解决,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/319485/

相关文章:

java - 用 Java 编写安全客户端

Java并发 - 使用信号量实现监视器 - 所有线程都陷入等待状态,我的理解有问题

c# - 使用 Mutex 'using' 时 Try-Finally-Release 是否多余?

dictionary - 使用 sync.Map 确保只有一个 goroutine 正在运行

android - 如何以编程方式使用智能锁 Lollipop 关闭屏幕

sql - 了解 Snowflake 中的锁和查询状态(对单个表的多次更新)

java - 如何调试 Java UnsatisfiedLinkError?

java - Maven:构建可运行的 jar,然后使用 maven-assembly-plugin 添加到 zip

java - org.springframework.security.authentication.encoding 在 maven 中不存在

c# - 由于版本列为空,无法保存实体。 NHibernate