This is the 10th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

A, problem,

Application scenarios

When an application sends an asynchronous command through the framework, the command execution result is returned asynchronously rather than immediately.

So, the question is, for this kind of asynchronous call in the application, can we get the execution result of the command immediately like synchronous call, how to achieve asynchronous to synchronous?

Second, the analysis

First, explain synchronous and asynchronous

  • Synchronization is when a call is made that does not return or continue until the result is received.

  • Asynchronous, when an asynchronous procedure call is issued, the caller can proceed without receiving the result. When the call is complete, the caller is generally notified through status, notifications, and callbacks.

For asynchronous calls, the return of the call is not controlled by the caller.

The main implementation roadmap is as follows: Block in the calling thread and wait for the result. After the call is complete, the blocking state is relieved by callback, setting the shared state or notification, and subsequent operations are continued.

Third, implementation method

In general, implementations do not wait indefinitely, and a timeout period is set, depending on the scenario.

The following is a callback to introduce several common methods to implement asynchronous synchronization:

1. Polling and hibernation retry

Using the polling and hibernation retry mechanism, threads will repeatedly switch between hibernation and test status conditions until timeout or the status condition is satisfied and continue to execute. In this way, the timeout is not controlled accurately, and sleep times need to be trade-offs between responsiveness and CPU utilization.

private static long MILLIS_OF_WAIT_TIME = 300000L; Private final Object lock = new Object(); //3. Perform a callback after the result is returned. @override public void callback(AsynResponse response){synchronized(lock){synchronized(lock)} public Result getResult() throws ErrorCodeException { // 1. Long Future = system.currentTimemillis () + MILLIS_OF_WAIT_TIME; long remaining = MILLIS_OF_WAIT_TIME; While (remaining > 0){synchronized(lock){if(remaining = future system.currentTimemillis (); // Remaining time while(remaining > 0){synchronized(lock){remaining = future system.currentTimemillis (); Thread.sleep(time is determined in specific scenarios); }} ````} //4. }Copy the code

2.wait/notify

Any Java object has a set of monitor methods (wait, notify, and notifyAll) that, in conjunction with the synchronized synchronization keyword, implement the wait/notify mode. However, with wait/notify, the blocking/awakening of a thread is passive to the thread itself. It is difficult to accurately control which thread is waiting on the conditional queue (notify), so it is necessary to wake up either one thread randomly (notifyAll, which is inefficient). When multiple threads are waiting on the same conditional queue based on different conditions, if you use notify instead of notifyAll, signal loss may occur easily. Therefore, you must use Wait /notify with caution.

private static long MILLIS_OF_WAIT_TIME = 300000L;// The wait time is 5 minutes
private final Object lock = new Object();

//3. Perform a callback after the result is returned to clear the block
@Override
public void callback(AsynResponse response){
    synchronized(lock){
        lock.notifyAll();
}
 
public Result getResult(a) throws ErrorCodeException {
	// 1. Asynchronous invocation
 
	// 2. Block waiting for an asynchronous response
    long future = System.currentTimeMillis() + MILLIS_OF_WAIT_TIME;
    long remaining = MILLIS_OF_WAIT_TIME;// Remaining waiting time
    synchronized(lock){
        while(The condition is not satisfied && Remaining >0) {// Check the condition when notifiedlock.wait(remaining); remaining = future - System.currentTimeMillis(); } ` ` ` `}//4. Time out or the result is returned correctly
    return result;
}
Copy the code

3.Lock Condition

Condition queues using Lock are implemented in a similar manner to wait/notify, but Lock supports multiple Condition queues and interrupts in wait states.

private static long SECONDS_OF_WAIT_TIME = 300L;// The wait time is 5 minutes
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();

//3. Perform a callback after the result is returned to clear the block
@Override
public void callback(AsynResponse response){
    lock.lock();// This is the premise
    try {
        condition.signal();
    }finally{ lock.unlock(); }}public Result getResult(a) throws ErrorCodeException {
	// 1. Asynchronous invocation
	// 2. Block waiting for an asynchronous response
    lock.lock();// This is the premise
    try {
        condition.await();
    } catch (InterruptedException e) {
        //TODO
    }finally {
        lock.unlock();
    }
	//4. Time out or the result is returned correctly
    return result;
}
Copy the code

4.CountDownLatch

CountDownLatch is an asynchronous to synchronous implementation, which acts as a counter. The number is passed in when an instance of the CountDownLatch object is created, and the count decreases by 1 every time the CountDownLatch () method is used. When the number decreases to 0, the code behind the await() method will be ready to execute. Blocks and waits until it reaches 0.

private static long SECONDS_OF_WAIT_TIME = 300L;// The wait time is 5 minutes
private final CountDownLatch countDownLatch = new CountDownLatch(1);

//3. Perform a callback after the result is returned to clear the block
@Override
public void callback(AsynResponse response){
    countDownLatch.countDown();
}

public Result getResult(a) throws ErrorCodeException {
    // 1. Asynchronous invocation

    // 2. Block waiting for an asynchronous response
    try {
        countDownLatch.await(SECONDS_OF_WAIT_TIME, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        //TODO
    }
	//4. Time out or the result is returned correctly
    return result;
}
Copy the code

5.CyclicBarrier

Let a group of threads block when they reach a barrier (also known as a synchronization point), and wait until the last thread reaches the barrier before the barrier opens and all threads blocked by the barrier continue to execute.

Each thread tells the CyclicBarrier that I have reached the barrier by calling the await method, and the current thread blocks.

private static long SECONDS_OF_WAIT_TIME = 300L;// The wait time is 5 minutes
private final CountDownLatch cyclicBarrier= new CyclicBarrier(2);// Set the number of threads blocked by the barrier to 2

//3. Perform a callback after the result is returned to clear the block
@Override
public void callback(AsynResponse response){
    // I have also reached the barrier and can open the door
    cyclicBarrier.await();
}

public Result getResult(a) throws ErrorCodeException {
    // 1. Asynchronous invocation
    // 2. Block waiting for an asynchronous response
    try {
        // I'm at the barrier. It's not open yet
        cyclicBarrier.await(SECONDS_OF_WAIT_TIME, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        //TODO
    }
	//4. Time out or the result is returned correctly
    return result;
}
Copy the code

The CountDownLatch and CyclicBarrier implementations are similar, except that CountDownLatch’s counter can only be used once, while CyclicBarrier’s counter can be reset.

So CyclicBarrier can handle more complex business scenarios. In asynchronous to synchronous, the counter is not reused, so the CountDownLatch implementation is more appropriate.

6.LockSupport

LockSupport defines a set of common static methods that provide the most basic ways to block and wake up threads.

private static long NANOS_OF_WAIT_TIME = 300000000L;// The wait time is 5 minutes
private final LockSupport lockSupport = new LockSupport();

//3. Perform a callback after the result is returned to clear the block
@Override
public void callback(AsynResponse response){
    lockSupport.unpark();
}

public Result getResult(a) throws ErrorCodeException {
    // 1. Asynchronous invocation

    // 2. Block waiting for an asynchronous response
    try {
        lockSupport.parkNanos(NANOS_OF_WAIT_TIME);
    } catch (InterruptedException e) {
        //TODO
    }
	//4. Time out or the result is returned correctly
    return result;

}
Copy the code

Learn more today, and less beg tomorrow! Come on