序言

上一节咱们学习了异步查问转同步的 7 种实现形式,明天咱们就来学习一下,如何对其进行封装,使其成为一个更加便于应用的工具。

思维导图如下:

拓展浏览

java 手写并发框架(1)异步查问转同步的 7 种实现形式

异步转同步的便利性

实现形式

  • 循环期待
  • wait & notify
  • 应用条件锁
  • 应用 CountDownLatch
  • 应用 CyclicBarrier
  • Future
  • Spring EventListener

上一节咱们曾经对下面的 7 种实现形式进行了具体的介绍,没有看过的同学能够去简略回顾一下。

然而这样集体感觉还是不够不便,懈怠是提高的阶梯。

更进一步简化

咱们心愿达到上面的成果:

@Syncpublic String queryId() {    System.out.println("开始查问");    return id;}@SyncCallback(value = "queryId")public void queryIdCallback() {    System.out.println("回调函数执行");    id = "123";}

通过注解间接须要同步的办法,和回调的办法,代码中间接调用即可。

咱们首先实现基于字节码加强的版本,后续将实现整合 spring, springboot 的版本。

锁的代码实现

锁的定义

咱们将原来的实现形象为加锁和解锁,为了便于拓展,接口定义如下:

package com.github.houbb.sync.api.api;/** * @author binbin.hou * @since 0.0.1 */public interface ISyncLock {    /**     * 期待策略     * @param context 上下文     * @since 0.0.1     */    void lock(final ISyncLockContext context);    /**     * 解锁策略     * @param context 上下文     * @since 0.0.1     */    void unlock(final ISyncUnlockContext context);}

其中上下文加锁和解锁做了辨别,不过临时内容是一样的。

次要是超时工夫和单位:

package com.github.houbb.sync.api.api;import java.util.concurrent.TimeUnit;/** * @author binbin.hou * @since 0.0.1 */public interface ISyncLockContext {    /**     * 超时工夫     * @return 后果     */    long timeout();    /**     * 超时工夫单位     * @return 后果     */    TimeUnit timeUnit();}

锁策略实现

咱们本节次要实现下上一节中的几种锁实现。

目前咱们抉择其中的是个进行实现:

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;/** * 期待告诉同步 * * @author binbin.hou * @since 0.0.1 */public class WaitNotifyLock implements ISyncLock {    private static final Log log = LogFactory.getLog(WaitNotifyLock.class);    /**     * 申明对象     */    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("进入期待,超时工夫为:{}ms", timeoutMills);                lock.wait(timeoutMills);            } catch (InterruptedException e) {                log.error("中断异样", e);                throw new SyncRuntimeException(e);            }        }    }    @Override    public void unlock(ISyncUnlockContext context) {        synchronized (lock) {            log.info("唤醒所有期待线程");            lock.notifyAll();        }    }}

加锁的局部比较简单,咱们从上下文中获取超时工夫和超时单位,间接和上一节内容相似,调用即可。

至于上下文中的信息是怎么来的,咱们后续就会解说。

条件锁实现

这个在有了上一节的根底之后也非常简单。

外围流程:

(1)创立锁

(2)获取锁的 condition

(3)执行加锁和解锁

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;/** * 期待告诉同步 * * @author binbin.hou * @since 0.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("程序进入锁定状态");            condition.await(context.timeout(), context.timeUnit());        } catch (InterruptedException e) {            log.error("程序锁定状态异样", e);        } finally {            lock.unlock();        }    }    @Override    public void unlock(ISyncUnlockContext context) {        lock.lock();        try{            log.info("解锁状态,唤醒所有期待线程。");            condition.signalAll();        } finally {            lock.unlock();        }    }}

CountDownLatch 实现

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;/** * 期待告诉同步 * * @author binbin.hou * @since 0.0.1 */public class CountDownLatchLock implements ISyncLock {    private static final Log log = LogFactory.getLog(CountDownLatchLock.class);    /**     * 闭锁     * 调用1次,后续办法即可通行。     */    private CountDownLatch countDownLatch = new CountDownLatch(1);    @Override    public synchronized void lock(ISyncLockContext context) {        countDownLatch = new CountDownLatch(1);        try {            log.info("进入期待,超时工夫为:{},超时单位:{}", context.timeout(),                    context.timeUnit());            boolean result = countDownLatch.await(context.timeout(), context.timeUnit());            log.info("期待后果: {}", result);        } catch (InterruptedException e) {            log.error("锁中断异样", e);        }    }    @Override    public void unlock(ISyncUnlockContext context) {        log.info("执行 unlock 操作");        countDownLatch.countDown();    }}

留神:这里为了保障 countDownLatch 能够屡次应用,咱们在每一次加锁的时候,都会从新创立 CountDownLatch。

CyclicBarrierLock 锁实现

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 * @since 0.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("重置 cyclicBarrier");            cyclicBarrier.reset();        } catch (InterruptedException | BrokenBarrierException | TimeoutException e) {            log.error("锁定时遇到异样", e);            throw new SyncRuntimeException(e);        }    }    @Override    public void unlock(ISyncUnlockContext context) {        try {            log.info("解锁信息");            cyclicBarrier.await(context.timeout(), context.timeUnit());        } catch (InterruptedException | TimeoutException | BrokenBarrierException e) {            log.error("解锁时遇到异样", e);        }    }}

这里和 CountDownLatchLock 的实现十分相似,不过 CyclicBarrier 有一个益处,就是能够复用。

咱们在每一次解锁之后,重置一下栅栏:

log.info("重置 cyclicBarrier");cyclicBarrier.reset();

锁的工具类

为了简略的生成上述几种锁的实例,咱们提供了一个简略的工具类办法:

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;/** * 锁策略 * @author binbin.hou * @since 0.0.1 */public final class Locks {    private Locks(){}    /**     * MAP 信息     * @since 0.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());    }    /**     * 获取锁实现     * @param lockType 锁类型     * @return 实现     * @since 0.0.1     */    public static ISyncLock getLock(final LockType lockType) {        return MAP.get(lockType);    }    /**     * @since 0.0.1     * @return 实现     */    private static ISyncLock waitNotify() {        return Instances.singleton(WaitNotifyLock.class);    }    /**     * @since 0.0.1     * @return 实现     */    private static ISyncLock countDownLatch() {        return Instances.singleton(CountDownLatchLock.class);    }    /**     * @since 0.0.1     * @return 实现     */    private static ISyncLock lockCondition() {        return Instances.singleton(LockConditionLock.class);    }    /**     * @since 0.0.1     * @return 实现     */    private static ISyncLock cyclicBarrier() {        return Instances.singleton(CyclicBarrierLock.class);    }}

上述的锁实现都是线程平安的,所以全副应用单例模式创立。

LockType 类是一个锁的枚举类,会在注解中应用。

小结

好了,到这里咱们就把上一节中的常见的 4 种锁策略就封装实现了。

你可能好奇上下文的工夫信息哪里来?这些锁又是如何被调用的?

咱们将通过注解+字节码加强的形式来实现调用(就是 aop 的原理),因为篇幅起因,字节码篇幅较长,为了浏览体验,实现局部将放在下一节。

感兴趣的能够关注一下,便于实时接管最新内容。

感觉本文对你有帮忙的话,欢送点赞评论珍藏转发一波。你的激励,是我最大的能源~

不晓得你有哪些播种呢?或者有其余更多的想法,欢送留言区和我一起探讨,期待与你的思考相遇。

文中如果链接生效,能够点击 {浏览原文}。