如何在定時(shí)任務(wù)執(zhí)行的過程中動(dòng)態(tài)的對(duì)其進(jìn)行修改

場(chǎng)景

在某一個(gè)時(shí)間段赠堵,系統(tǒng)有定時(shí)任務(wù)會(huì)對(duì)某一張表的數(shù)據(jù)進(jìn)行處理小渊,在處理的這時(shí)候可能會(huì)有其他的服務(wù)會(huì)來對(duì)這個(gè)表進(jìn)行操作。如果他們操作了同一個(gè)行記錄茫叭,這個(gè)時(shí)間點(diǎn)上就會(huì)出現(xiàn)數(shù)據(jù)不一致的可能性酬屉。所以需要對(duì)這種情況進(jìn)行處理。

解決方案

背景介紹:任務(wù)當(dāng)前是對(duì)表數(shù)據(jù)進(jìn)行遍歷取出來揍愁,然后逐條進(jìn)行執(zhí)行呐萨。

方案一

這種方案是最簡(jiǎn)單,改造成本最低的莽囤。我們可以加一個(gè)變量來標(biāo)識(shí)任務(wù)是否有在執(zhí)行的過程中谬擦,執(zhí)行的過程中拒絕其他服務(wù)的調(diào)用即可。這里就不展開贅述了朽缎。

方案二

這個(gè)方案的設(shè)計(jì)初衷就是標(biāo)題所述惨远,想在定時(shí)任務(wù)的執(zhí)行過程中蔚舀,其他服務(wù)也可以進(jìn)行調(diào)用。但是其他服務(wù)要先來定時(shí)任務(wù)處進(jìn)行查詢是否已被處理锨络,未被執(zhí)行到的行記錄就更新下狀態(tài)赌躺,讓定時(shí)任務(wù)跳過當(dāng)條記錄,已處理的數(shù)據(jù)則拒絕更新羡儿,來保證數(shù)據(jù)的一致性礼患。

定時(shí)任務(wù)的流程圖大概是這樣的
1.加載資源處理成map
2.遍歷每一條記錄
3.判斷該記錄是否被其他服務(wù)更新狀態(tài),是則跳過掠归,否則繼續(xù)處理
st=>start: 開始
op1=>operation: 處理資源成map
op2=>operation: 遍歷執(zhí)行每一條記錄(判斷狀態(tài))
cond1=>condition: 該條記錄是否被更新狀態(tài)
op3=>operation: 繼續(xù)處理該條記錄


e=>end: 結(jié)束

st->op1->op2->cond1->op3->e
cond1(no)->op3
cond1(yes)->op2

改造思路

定時(shí)任務(wù)部分

  1. 將原有的list存儲(chǔ)換成ConcurrentHashMap以支持多線程的操作缅叠,結(jié)構(gòu)以主鍵作為key,value是處理的表對(duì)象 + 該條記錄的狀態(tài)(0-未處理虏冻, 1-處理中肤粱, 2-已處理)。
  2. 將封裝的ConcurrentHashMap對(duì)外暴露以給其他接口服務(wù)進(jìn)行調(diào)用厨相。
  3. 遍歷的時(shí)候领曼,對(duì)該條記錄的狀態(tài)進(jìn)行判斷,對(duì)狀態(tài)被更新為2的記錄進(jìn)行不處理蛮穿。
    ps:這里狀態(tài)為2的記錄就是被其他服務(wù)修改的庶骄,然后通知給定時(shí)任務(wù)。

其他服務(wù)接口

  1. 查詢這個(gè)ConcurrentHashMap是否當(dāng)前記錄在定時(shí)任務(wù)的處理中践磅,不在則可以直接使用单刁,在則對(duì)狀態(tài)進(jìn)行更新為2,通知定時(shí)任務(wù)不可以繼續(xù)處理了府适。

存在的問題
這種方案設(shè)計(jì)完成后羔飞,我深思了一下。發(fā)現(xiàn)無(wú)法保證同時(shí)間同一個(gè)key在同時(shí)get和put的時(shí)候執(zhí)行的順序檐春,所以會(huì)存在這種邊界問題(我自己這么稱之)逻淌。雖然ConcurrentHashMap是線程安全的,但是它任然無(wú)法滿足我的要求喇聊,如下偽代碼所示恍风。

//其他服務(wù)接口
line = map.get(key); //這里先取到
if(line.state == 0){ //滿足未執(zhí)行的時(shí)候蹦狂,進(jìn)行更新
    //更新state=2;
}

get和put執(zhí)行的區(qū)間誓篱,定時(shí)任務(wù)同時(shí)也進(jìn)行了該條行記錄的數(shù)據(jù),這就會(huì)產(chǎn)生數(shù)據(jù)不一致的問題凯楔。
方案二其實(shí)無(wú)法保證數(shù)據(jù)的一致性窜骄,在稍微極端的情況下。于是就有了方案三摆屯,基于方案二的迭代思路邻遏。

方案三

在方案二中我們知道了極端情況下糠亩,數(shù)據(jù)會(huì)存在不一致的問題。是因?yàn)閿?shù)據(jù)在第一次被其他服務(wù)接口讀取后准验,又在定時(shí)任務(wù)的循環(huán)中被讀取了赎线,重新進(jìn)行處理。那么我們對(duì)這里進(jìn)行一定的改造是不是就可以了糊饱。順著這個(gè)思路我就繼續(xù)往下想垂寥。

然后還真被我想到了處理方法,也是我最近在學(xué)習(xí)spring源碼的過程中得到到想法另锋,我可以對(duì)被讀取的數(shù)據(jù)進(jìn)行封裝滞项,不直接是行記錄的對(duì)象,正如spring-beans中的beanbeanWrapper的關(guān)系**

封裝類偽代碼如下夭坪。

public class LineWrapper<T> {
    private T line; //封裝的行對(duì)象
    private volatile int state = 0; //計(jì)數(shù)器
    private ReentrantLock lock = new ReentrantLock(); //鎖

    public LineWrapper(T line){
        this.line = line;
    }

    public T getLine(){
        //state != 0則表表明不是第一次就不用進(jìn)行鎖的判斷可以直接返回文判,如果第一次則進(jìn)行嘗試加鎖
        if(state == 0 && lock.tryLock()){
            lock.lock(); //加鎖
            state++; //增加計(jì)數(shù)器
            return line; //返回存在的對(duì)象
        }
        return null; //否則返回null,這樣不管是定時(shí)任務(wù)還是其他服務(wù)接口都不進(jìn)行處理
        //TODO 在finally進(jìn)行鎖的釋放
    }
}

這樣設(shè)計(jì)之后就可以保證在極端的情況下也不會(huì)出現(xiàn)該行記錄被操作兩次室梅,同時(shí)也可以拋棄掉ConcurrentHashMap戏仓,使用HashMap,來提高一些性能亡鼠。

Ps

方案三默認(rèn)是單機(jī)版存儲(chǔ)在內(nèi)存中的
如果在微服務(wù)的架構(gòu)下柜去,可以使用 redis + 分布式鎖 來進(jìn)行替換

最終

finally,選取了第一種方案(好失落安鹜稹)嗓奢,雖然第三種方案更加的完善,支持同時(shí)的處理浑厚。但是它的改造成本要高于第一種方案不少股耽,同時(shí)復(fù)雜性提升也會(huì)帶來更多可能出現(xiàn)的問題。這里雖然只選對(duì)的不選擇最好的道理钳幅,但是我們同樣也不要放棄思考更優(yōu)的解決方案物蝙。


如果有不對(duì)的,也請(qǐng)多多指正敢艰。畢竟一家之見诬乞,多有偏頗。
謝謝

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钠导,一起剝皮案震驚了整個(gè)濱河市震嫉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牡属,老刑警劉巖票堵,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異逮栅,居然都是意外死亡悴势,警方通過查閱死者的電腦和手機(jī)窗宇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來特纤,“玉大人军俊,你說我怎么就攤上這事∨醮妫” “怎么了蝇完?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)矗蕊。 經(jīng)常有香客問我短蜕,道長(zhǎng),這世上最難降的妖魔是什么傻咖? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任朋魔,我火速辦了婚禮,結(jié)果婚禮上卿操,老公的妹妹穿的比我還像新娘警检。我一直安慰自己,他們只是感情好害淤,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布扇雕。 她就那樣靜靜地躺著,像睡著了一般窥摄。 火紅的嫁衣襯著肌膚如雪镶奉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天崭放,我揣著相機(jī)與錄音哨苛,去河邊找鬼。 笑死币砂,一個(gè)胖子當(dāng)著我的面吹牛建峭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播决摧,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼亿蒸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了掌桩?” 一聲冷哼從身側(cè)響起边锁,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拘鞋,沒想到半個(gè)月后砚蓬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矢门,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盆色,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年灰蛙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隔躲。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摩梧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宣旱,到底是詐尸還是另有隱情仅父,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布浑吟,位于F島的核電站笙纤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏组力。R本人自食惡果不足惜省容,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望燎字。 院中可真熱鬧腥椒,春花似錦、人聲如沸候衍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蛉鹿。三九已至滨砍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妖异,已是汗流浹背惨好。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留随闺,地道東北人日川。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像矩乐,于是被迫代替她去往敵國(guó)和親龄句。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容