不可重入鎖和可重入鎖

類(lèi):BccSoaHelper
public startLockOnlyOne() throws Exception{

        String billCacheKey = createCacheKey(billId);
        try {
            // 第一層控制:手機(jī)號(hào)碼
            addKeyAndValue2AllServer(billCacheKey, billCacheKey, expireSeconds);
        } catch (BusinessConcurrentControlException e) {
            ExceptionUtil.throwBusinessException(BsI18nResource.SOS11120180, billId);
        }
}

這個(gè)方法在下面的一個(gè)場(chǎng)景里面執(zhí)行不會(huì)有問(wèn)題,都可以很好的處理兩個(gè)業(yè)務(wù)對(duì)同一個(gè)號(hào)碼互斥執(zhí)行的需求,如果兩個(gè)方法并發(fā)執(zhí)行,都進(jìn)入到了臨界區(qū),那么會(huì)爭(zhēng)用該全局鎖,最后只有一個(gè)方法會(huì)執(zhí)行成功;另外一個(gè)方法會(huì)直接拋異常,事務(wù)回滾,告知調(diào)用方此時(shí)有另外一個(gè)業(yè)務(wù)在執(zhí)行,暫時(shí)不能執(zhí)行當(dāng)前業(yè)務(wù)

public method1OnSv1() throws Exception{
...臨界區(qū)

    BccSoaHelper.startLockOnlyOne();
...
}

public method2OnSv2() throws Exception{
...臨界區(qū)
    BccSoaHelper.startLockOnlyOne();
...
}

假如某個(gè)SV層同時(shí)調(diào)用了兩次這個(gè)方法,這個(gè)SV的代碼會(huì)因?yàn)閽伋霎惓;貪L,此時(shí)我們可以說(shuō)這個(gè)鎖是不可重入

public method1OnSv1() throws Exception{
...臨界區(qū)
    ...假如某個(gè)邏輯里面執(zhí)行了第一次訂單受理
     //
     BccSoaHelper.startLockOnlyOne();
    ...假如某個(gè)邏輯里面執(zhí)行了第二次訂單受理,必拋異常
     //
     BccSoaHelper.startLockOnlyOne();
...
}

所以要對(duì)其再進(jìn)行再一步的加深區(qū)分,使其可以重入;一般來(lái)說(shuō),出現(xiàn)上面所說(shuō)的不可重入的情況的,都應(yīng)該是在“同一個(gè)方法”執(zhí)行過(guò)程中調(diào)用了兩次該鎖,而我們的SOA里面的SV的調(diào)用應(yīng)該就是與該“同一個(gè)方法”類(lèi)似,在這個(gè)SOA調(diào)用里面因?yàn)榭赡苷{(diào)用不同的中心不同的業(yè)務(wù)SV,而這個(gè)業(yè)務(wù)SV是否調(diào)用了該全局鎖是不清楚的,所以有可能會(huì)因?yàn)樵撴i在同一個(gè)SOA的SV里面不可重入導(dǎo)致事務(wù)回滾,所以要實(shí)現(xiàn)同一個(gè)SOA調(diào)用里面的可重入。

然后巧妙的使用session管理器實(shí)現(xiàn)“同一個(gè)SOA”里面的可重入,因?yàn)閟ession的生命周期剛好就貫穿整個(gè)SOA調(diào)用,使用其保存一些額外信息來(lái)區(qū)分是否是同一個(gè)SOA里面的調(diào)用正好不過(guò),如果判斷是同一個(gè)SOA第二次或者更多的對(duì)鎖的調(diào)用,那么就不拋出異常,這樣就可以實(shí)現(xiàn)可重入了,下面是完整的代碼

public static void beginBusiCtrlForJX(String billId, long mainBusinessId, short expireSeconds) throws Exception {       
        String billCacheKey = createCacheKey(billId);
        String soaInvokeCacheKey = createCacheKey(getSoaInvokeBccUniqueIndex(billId));
        try {
            // 第一層控制:手機(jī)號(hào)碼
            addKeyAndValue2AllServer(billCacheKey, billCacheKey, expireSeconds);
        } catch (BusinessConcurrentControlException e) {
            // 會(huì)進(jìn)入此分支,代表已經(jīng)通過(guò)了BillId進(jìn)行了并發(fā)控制
            try {
                addKeyAndValue2AllServer(soaInvokeCacheKey, soaInvokeCacheKey, expireSeconds);
            } catch (BusinessConcurrentControlException e2) {// 如果此時(shí)拋異常,說(shuō)明是單一SOA服務(wù)調(diào)用,同一session的同一個(gè)的第二次SOA調(diào)用也會(huì)在這里
                log.info("<<<<<<<<<<<<<<<<<<<<<單一SOA服務(wù)調(diào)用, 屬于正常場(chǎng)景>>>>>>>>>>>>>>>>>>>>>");
                return;
            }
            try {
                BccUtil.delete(soaInvokeCacheKey);
            } catch (Exception e2) {
                log.error("<<<<<<<<<<<<<<<<<<<<<用戶(hù)正在受理其他訂單類(lèi)業(yè)務(wù),請(qǐng)稍后再試!>>>>>>>>>>>>>>>>>>>>>");
            }
            ExceptionUtil.throwBusinessException(BsI18nResource.SOS11120180, billId);
        } catch (Exception e) {
            log.error(ExceptUtil.getExceptionDtl(e));
        }
        try {
            addKeyAndValue2AllServer(soaInvokeCacheKey, soaInvokeCacheKey, expireSeconds);
        } catch (Exception e) {
            log.error("<<<<<<<<<<<<<<<<<<<<<并發(fā)控制失敗>>>>>>>>>>>>>>>>>>>>>");
        }
    }


/**
     * 獲取Soa服務(wù)調(diào)用對(duì)應(yīng)的BCC并發(fā)控制唯一索引
     * 
     * @param billId
     * @return
     * @throws Exception
     */
    private static String getSoaInvokeBccUniqueIndex(String billId) throws Exception {
        Object bccUniqueIndexObject = SessionManager.getUser().get(BCC_UNIQUE_INDEX);
        String bccUniqueIndex = StringUtils.EMPTY;
        if (null == bccUniqueIndexObject) {
            bccUniqueIndex = generateBccUniqueIndex(billId);// 緩存中沒(méi)有,則生成一條唯一索引值
            SessionManager.getUser().set(BCC_UNIQUE_INDEX, bccUniqueIndex);
        } else {
            bccUniqueIndex = (String) bccUniqueIndexObject;
        }
        return bccUniqueIndex;
    }

備注:session必須是全局共享的或者是每次都可以通過(guò)負(fù)載均衡到同一個(gè)web容器的,同時(shí),如果memcached的異常則此鎖會(huì)直接拋異常

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容