上一節咱們學習了異步查詢轉同步的 7 種實現方式,今天咱們就來學習一下,如何對其進行封裝,使其成爲一個更加便於使用的工具。java
思惟導圖以下:git
java 手寫併發框架(1)異步查詢轉同步的 7 種實現方式github
循環等待spring
wait & notifyapi
使用條件鎖安全
使用 CountDownLatchspringboot
使用 CyclicBarrier併發
Future框架
上一節咱們已經對上面的 7 種實現方式進行了詳細的介紹,沒有看過的同窗能夠去簡單回顧一下。異步
可是這樣我的以爲仍是不夠方便,懶惰是進步的階梯。
咱們但願達到下面的效果:
@Sync public 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(); }
咱們本節主要實現下上一節中的幾種鎖實現。
目前咱們選擇其中的是個進行實現:
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(); } } }
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。
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 的原理),因爲篇幅緣由,字節碼篇幅較長,爲了閱讀體驗,實現部分將放在下一節。
感興趣的能夠關注一下,便於實時接收最新內容。
以爲本文對你有幫助的話,歡迎點贊評論收藏轉發一波。你的鼓勵,是我最大的動力~
不知道你有哪些收穫呢?或者有其餘更多的想法,歡迎留言區和我一塊兒討論,期待與你的思考相遇。
文中若是連接失效,能夠點擊 {閱讀原文}。