Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

multithread: java condition await timeout but can't return

Lock sharedLock = new ReentrantLock();
Condition condition = lock.newCondition();

main thread:

sharedLock.lock();
childThread.start();
condition.await(5, TimeUnit.SECONDS);
sharedLock.unlock();

child thread:

sharedLock.lock();
//do something, may take a long time
Thread.sleep(10);// sleep to simulate a long execution
condition.signal();
sharedLock.unlock();

Suppose child thread send a network request and wait for response, I want main thread wait at most 5 seconds, if timeout, retry the request. but when the await() timeout, it cannot acquire lock because child thread still hold it, so it still wait the lock until child thread release it, which takes 10 seconds.

How can I achieve my requirement that main thread wait child thread's signal, but have a bounded timeout?

like image 395
Moon Avatar asked Sep 06 '26 01:09

Moon


1 Answers

This is not how your are supposed to do it, you are supposed to:

  1. Create an ExecutorService (thread pool) for that you should check the methods of the class Executors to choose the best one in your case but Executors.newFixedThreadPool is a good start
  2. Submit your task as a FutureTask to the thread pool
  3. Then call get with a timeout
  4. Manage properly the TimeoutException

Here is how it could be done:

// Total tries
int tries = 3;
// Current total of tries
int tryCount = 1;
do {
    // My fake task to execute asynchronously
    FutureTask<Void> task = new FutureTask<>(
        () -> {
            Thread.sleep(2000);
            return null;
        }
    );
    // Submit the task to the thread pool
    executor.submit(task);
    try {
        // Wait for a result during at most 1 second
        task.get(1, TimeUnit.SECONDS);
        // I could get the result so I break the loop
        break;
    } catch (TimeoutException e) {
        // The timeout has been reached
        if (tryCount++ == tries) {
            // Already tried the max allowed so we throw an exception
            throw new RuntimeException(
                String.format("Could execute the task after %d tries", tries),
                e
            );
        }
    }
} while (true);

How can I achieve my requirement that main thread wait child thread's signal, but have a bounded timeout?

Here is how you can achieve your requirements:

Main Thread:

lock.lock();
try {
    childThread.start();
    condition.await(5, TimeUnit.SECONDS);
} finally {
    sharedLock.lock();
}

The child thread:

try {
    //do something, may take a long time
    Thread.sleep(10);// sleep to simulate a long execution
} finally {
    // Here we notify the main thread that the task is complete whatever
    // the task failed or not
    lock.lock();
    try {
        condition.signal();
    } finally {
        lock.unlock();
    }
}

As you can see to work, the task must not be performed within the critical section, we only acquire the lock to notify the main thread nothing more. Otherwise if you execute the task within the critical section after the timeout the main thread will still need to acquire the lock once again and since the lock is actually owned by the child thread, it will need to wait anyway until the end of the task which makes the timeout totally useless.

NB: I renamed sharedLock to lock as a ReentrantLock is an exclusive lock not as shared lock, if you need a shared lock check the class Semaphore to define the total amount of permits.

like image 134
Nicolas Filotto Avatar answered Sep 07 '26 15:09

Nicolas Filotto



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!