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