【java】CountDownLatch運用場景(1)

post200發表於2021-09-09

基本功能

CountDownLatch也叫閉鎖,使得一(多)個主執行緒必須等待其他執行緒完成操作後再執行。
使用的方式是:CountDownLatch內部維護一個計數器,主執行緒先執行await方法,如果此時計數器大於0,則阻塞等待。當一個執行緒完成任務後,計數器值減1。直到計數器為0時,表示所有的執行緒已經完成任務,等待的主執行緒被喚醒繼續執行。
應用場景:應用程式的主執行緒希望在負責啟動框架服務的執行緒已經完成之後再執行。

應用:快取載入

在廣告的核心引擎中,我們的服務需要載入很多快取資料,載入完成之後,主執行緒才能啟動對外提供服務。這個時候我們就用到了CountDownLatch來定時載入快取。快取載入的東西我們之後再單獨開帖子講,這裡先看CountDownLatch的使用。

  • 定義載入快取的job抽象類

public abstract class BaseCacheUpdateJob {    //job的名字
    public String name() {        return this.getClass().getSimpleName();
    }    //job的執行週期
    public long getPeriodInSecond() {        return PERIOD_ONE_HOUR;
    }    //job的重要性
    public boolean isEssential() {        return false;
    }    //job的具體內容
    public abstract boolean update();
}
  • 實現需要的job

//載入App資料的cache.@Componentpublic class AppCache extends BaseCacheUpdateJob {    private Map<String, String> map = new HashMap<>();    @Autowired
    public AppCache() {
    }    @Override
    public long getPeriodInSecond() {        return PERIOD_ONE_MINUTE;
    }    public String getValueByKey(String appId) {        return map.getOrDefault(appId, "not find in appCache");
    }    @Override
    public boolean update() {
        map.put("add", "0");        return true;
    }
}//載入廣告資料的cache.@Componentpublic class AdCache extends BaseCacheUpdateJob {    private Map<String, String> map = new HashMap<>();    @Autowired
    public AdCache() {
    }    @Override
    public long getPeriodInSecond() {        return PERIOD_ONE_MINUTE;
    }    public String getValueByKey(String appId) {        return map.getOrDefault(appId, "not find in AdCache");
    }    @Override
    public boolean update() {
        map.put("add", "0");        return true;
    }
}// 載入使用者畫像的cache// 載入Ctr預估模型的cache// 載入黑白名單的cache// 載入配置項的cache// ...
  • 開始載入快取

上面兩步我們定義好了我們服務啟動的時候需要幹什麼事情,那麼具體怎麼幹,就交給了CountDownLatch

@Service@Slf4jpublic final class InterCacheService { 
    //這裡spring的自動注入會把定義好的Bean全部注入進來記憶體
    @Autowired
    private List<BaseCacheUpdateJob> cacheUpdateJobs;    
    @PostConstruct
    private void start() {        //定義執行緒池
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(cacheUpdateJobs.size());
        CountDownLatch completeTaskLatch = new CountDownLatch(cacheUpdateJobs.size());        
        for(BaseCacheUpdateJob job : cacheUpdateJobs) {            boolean loadStatus = job.update();            if (loadStatus) {
                countDownLatch.countDown();
            }
        }        //阻塞住,等待上面的載入完,才會執行主執行緒.
        completeTaskLatch.await();        //快取載入到記憶體中了,主執行緒可以繼續載入其他bean,完成之後提供服務.
    }
}

這裡只是舉個例子,簡化了很多程式碼,這種程式碼肯定不能在生產環境跑的,這麼跑肯定會出問題的。

比如:

1.考慮這麼一個場景,如果快取是一個不那麼重要的,你的服務其實是可以起來的。那麼如何管理這種狀態呢?

2.還有可能出現快取依賴的問題,載入AdCache需要依賴於AppCache,載入AppCache需要依賴BlackListCache,怎麼管理這種狀態呢?

3.快取沒載入成功,我什麼時候去嘗試呢?隔多久?

4.快取都在同一個時間點去載入,導致我線上的GC壓力比較大怎麼辦?

5.快取一般是多執行緒訪問的公共資源,那麼怎麼線上程安全和效能之間做取捨呢?

我後面單獨開幾篇帖子講快取,有興趣的小夥伴可以先看下這個小框架的原始碼。



作者:碼蹄疾
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2249/viewspace-2818120/,如需轉載,請註明出處,否則將追究法律責任。

相關文章