The preface

In the last section we looked at seven ways to implement asynchronous query synchronization. Today we’ll look at how to encapsulate it and make it a more user-friendly tool.

The mind map is as follows:

Develop reading

Java handwriting concurrency framework (1) 7 ways to realize asynchronous query to synchronization

Convenience of asynchronous to synchronous

implementation

  • Loop waiting for

  • wait & notify

  • Service condition lock

  • Using CountDownLatch

  • Use the CyclicBarrier

  • Future

  • Spring EventListener

In the last section, we covered all seven of these implementations in detail, so if you haven’t seen them, you can review them briefly.

But this personal feeling is not convenient enough, laziness is the ladder of progress.

Further simplification

We want to achieve the following results:

@Sync
public String queryId(a) {
    System.out.println("Start query");
    return id;
}

@SyncCallback(value = "queryId")
public void queryIdCallback(a) {
    System.out.println("Callback function execution");
    id = "123";
}
Copy the code

Methods that need to be synchronized directly through annotations, and methods that need to be called back directly from the code.

We will implement a bytecode-enhanced version first, followed by an integrated Spring, SpringBoot version.

Lock code implementation

The definition of the lock

We abstracted the original implementation into locking and unlocking. For easy expansion, the interface was defined as follows:

package com.github.houbb.sync.api.api;

/ * * *@author binbin.hou
 * @since0.0.1 * /
public interface ISyncLock {

    /** * wait policy *@paramContext *@since0.0.1 * /
    void lock(final ISyncLockContext context);

    /** * Unlock policy *@paramContext *@since0.0.1 * /
    void unlock(final ISyncUnlockContext context);

}
Copy the code

The context locks and unlocks are distinguished, but temporarily the content is the same.

Mainly timeout time and units:

package com.github.houbb.sync.api.api;

import java.util.concurrent.TimeUnit;

/ * * *@author binbin.hou
 * @since0.0.1 * /
public interface ISyncLockContext {

    /** * Timeout duration *@returnResults the * /
    long timeout(a);

    /** * Timeout duration unit *@returnResults the * /
    TimeUnit timeUnit(a);

}
Copy the code

Lock Policy Implementation

In this section we focus on implementing several lock implementations from the previous section.

Currently we choose one of them to implement:

wait & notify

package com.github.houbb.sync.core.support.lock;

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.sync.api.api.ISyncLock;
import com.github.houbb.sync.api.api.ISyncLockContext;
import com.github.houbb.sync.api.api.ISyncUnlockContext;
import com.github.houbb.sync.api.exception.SyncRuntimeException;

/** * wait for notification to synchronize **@author binbin.hou
 * @since0.0.1 * /
public class WaitNotifyLock implements ISyncLock {

    private static final Log log = LogFactory.getLog(WaitNotifyLock.class);

    /** * declare the object */
    private final Object lock = new Object();

    @Override
    public synchronized void lock(ISyncLockContext context) {
        synchronized (lock) {
            try {
                long timeoutMills = context.timeUnit().toMillis(context.timeout());
                log.info("Enter wait, timeout is: {}ms", timeoutMills);
                lock.wait(timeoutMills);
            } catch (InterruptedException e) {
                log.error("Interrupt exception", e);
                throw newSyncRuntimeException(e); }}}@Override
    public void unlock(ISyncUnlockContext context) {
        synchronized (lock) {
            log.info("Wake up all waiting threads"); lock.notifyAll(); }}}Copy the code

The lock part is relatively simple. We get the timeout time and timeout unit from the context, just like in the previous section.

How this information comes from context will be explained later.

Conditional lock implementation

This is also pretty simple given the basics of the last video.

Core process:

(1) Create lock

(2) Obtain the condition of the lock

(3) Perform lock and unlock

package com.github.houbb.sync.core.support.lock;

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.sync.api.api.ISyncLock;
import com.github.houbb.sync.api.api.ISyncLockContext;
import com.github.houbb.sync.api.api.ISyncUnlockContext;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** * wait for notification to synchronize **@author binbin.hou
 * @since0.0.1 * /
public class LockConditionLock implements ISyncLock {

    private static final Log log = LogFactory.getLog(LockConditionLock.class);

    private final Lock lock = new ReentrantLock();

    private final Condition condition = lock.newCondition();

    @Override
    public synchronized void lock(ISyncLockContext context) {
        lock.lock();
        try{
            log.info("The program is locked.");
            condition.await(context.timeout(), context.timeUnit());
        } catch (InterruptedException e) {
            log.error("Abnormal program lock status", e);
        } finally{ lock.unlock(); }}@Override
    public void unlock(ISyncUnlockContext context) {
        lock.lock();
        try{
            log.info("Unlock state, wake up all waiting threads.");
            condition.signalAll();
        } finally{ lock.unlock(); }}}Copy the code

CountDownLatch implementation

package com.github.houbb.sync.core.support.lock;

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.sync.api.api.ISyncLock;
import com.github.houbb.sync.api.api.ISyncLockContext;
import com.github.houbb.sync.api.api.ISyncUnlockContext;

import java.util.concurrent.CountDownLatch;

/** * wait for notification to synchronize **@author binbin.hou
 * @since0.0.1 * /
public class CountDownLatchLock implements ISyncLock {

    private static final Log log = LogFactory.getLog(CountDownLatchLock.class);

    /** * lock * once, subsequent methods can pass. * /
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    @Override
    public synchronized void lock(ISyncLockContext context) {
        countDownLatch = new CountDownLatch(1);

        try {
            log.info("Wait, timeout: {}, timeout unit: {}", context.timeout(),
                    context.timeUnit());
            boolean result = countDownLatch.await(context.timeout(), context.timeUnit());
            log.info("Wait for result: {}", result);
        } catch (InterruptedException e) {
            log.error("Lockbreak exception", e); }}@Override
    public void unlock(ISyncUnlockContext context) {
        log.info("Perform unlock operation"); countDownLatch.countDown(); }}Copy the code

Note: To ensure that countDownLatch can be used multiple times, we will re-create countDownLatch each time the lock is added.

CyclicBarrierLock locks

package com.github.houbb.sync.core.support.lock;

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.sync.api.api.ISyncLock;
import com.github.houbb.sync.api.api.ISyncLockContext;
import com.github.houbb.sync.api.api.ISyncUnlockContext;
import com.github.houbb.sync.api.exception.SyncRuntimeException;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeoutException;

/ * * *@author binbin.hou
 * @since0.0.1 * /
public class CyclicBarrierLock implements ISyncLock {

    private static final Log log = LogFactory.getLog(CyclicBarrierLock.class);

    private final CyclicBarrier cyclicBarrier = new CyclicBarrier(2);

    @Override
    public synchronized void lock(ISyncLockContext context) {
        try {
            log.info(Timeout :{}, timeUnit :{}",
                    context.timeout(), context.timeUnit());
            cyclicBarrier.await(context.timeout(), context.timeUnit());

            log.info("Reset cyclicBarrier");
            cyclicBarrier.reset();
        } catch (InterruptedException | BrokenBarrierException | TimeoutException e) {
            log.error("Exception encountered while locking", e);
            throw newSyncRuntimeException(e); }}@Override
    public void unlock(ISyncUnlockContext context) {
        try {
            log.info("Unlock Information");
            cyclicBarrier.await(context.timeout(), context.timeUnit());
        } catch (InterruptedException | TimeoutException | BrokenBarrierException e) {
            log.error("Exception encountered while unlocking", e); }}}Copy the code

This is very similar to CountDownLatchLock’s implementation, but CyclicBarrier has the advantage of being reusable.

We reset the fence after each unlock:

log.info("Reset cyclicBarrier");
cyclicBarrier.reset();
Copy the code

Lock utility class

To simply generate instances of the above locks, we provide a simple utility class method:

package com.github.houbb.sync.core.support.lock;

import com.github.houbb.heaven.support.instance.impl.Instances;
import com.github.houbb.sync.api.api.ISyncLock;
import com.github.houbb.sync.api.constant.LockType;

import java.util.HashMap;
import java.util.Map;

/** * Lock policy *@author binbin.hou
 * @since0.0.1 * /
public final class Locks {

    private Locks(a){}

    /** * MAP information *@since0.0.1 * /
    private static final Map<LockType, ISyncLock> MAP = new HashMap<>();

    static {
        MAP.put(LockType.WAIT_NOTIFY, waitNotify());
        MAP.put(LockType.COUNT_DOWN_LATCH, countDownLatch());
        MAP.put(LockType.CYCLIC_BARRIER, cyclicBarrier());
        MAP.put(LockType.LOCK_CONDITION, lockCondition());
    }

    /** * get lock implementation *@paramLockType lockType *@returnImplement *@since0.0.1 * /
    public static ISyncLock getLock(final LockType lockType) {
        return MAP.get(lockType);
    }

    / * * *@since 0.0.1
     * @returnImplement * /
    private static ISyncLock waitNotify(a) {
        return Instances.singleton(WaitNotifyLock.class);
    }

    / * * *@since 0.0.1
     * @returnImplement * /
    private static ISyncLock countDownLatch(a) {
        return Instances.singleton(CountDownLatchLock.class);
    }

    / * * *@since 0.0.1
     * @returnImplement * /
    private static ISyncLock lockCondition(a) {
        return Instances.singleton(LockConditionLock.class);
    }

    / * * *@since 0.0.1
     * @returnImplement * /
    private static ISyncLock cyclicBarrier(a) {
        returnInstances.singleton(CyclicBarrierLock.class); }}Copy the code

The above lock implementations are thread-safe, so they are all created using the singleton pattern.

The LockType class is an enumeration of locks that are used in annotations.

summary

Ok, so here we wrap up the four common locking strategies from the previous section.

You may wonder where the contextual time information comes from. How are these locks called?

We’ll implement the invocation (which is how AOP works) through annotations + bytecode enhancement, which is a bit long for space reasons, but the implementation section will be in the next section for reading experience.

Interested can pay attention to, facilitate real-time reception of the latest content.

If you find this article helpful, please click on it. Your encouragement, is my biggest motivation ~

Do not know what you have gained? Or have other more ideas, welcome to discuss with me in the comments section, looking forward to meeting your thinking.

If the link in the article fails, you can click {read the original text}.