[ Java ] CountDownLatch 运用场景(1) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Acceml
V2EX    Java

[ Java ] CountDownLatch 运用场景(1)

  •  
  •   Acceml
    Acceml 2018-08-28 21:54:41 +08:00 2340 次点击
    这是一个创建于 2613 天前的主题,其中的信息可能已经有所发展或是发生改变。

    基本功能

    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. @Component public 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. @Component public 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 @Slf4j public 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.缓存一般是多线程访问的公共资源,那么怎么在线程安全和性能之间做取舍呢?

    我后面单独开几篇帖子讲缓存,有兴趣的小伙伴可以先看下这个小框架的源码。

    源码

    https://github.com/Acceml/local_cache_manager

    1. 如果你校招 /1-3 年的社招,简历上没有项目的话,把这个项目吃透,可以拿出去和面试官吹牛逼的。前提是你理解我们为什么这么写。这个可比你写个什么爬虫有技术含量得多。
    2. 如果你是工作的小伙伴,你们的服务缓存有很好的管理机制吗? 可以参考下我们的实现方法。

    热门阅读


    最近在刷题,有一起的可以加手撕代码群:805423079 Leetcode 名企之路

    3 条回复    2018-08-29 09:42:12 +08:00
    skypyb
        1
    skypyb  
       2018-08-29 09:13:18 +08:00
    闭锁啊..用过几次,一般都是必须等待某一些线程 /任务执行完毕然后进行一些操作才用得上把 = =
    Acceml
        2
    Acceml  
    OP
       2018-08-29 09:15:02 +08:00
    @skypyb 缓存加载就是这么一个典型场景啊。网上举的例子都是什么开会要等待会议人员到场啥的,我觉得还是用一个生产环境的例子比较好。
    skypyb
        3
    skypyb  
       2018-08-29 09:42:12 +08:00
    @Acceml 闭锁的使用还是比较简单的。说的简单点,在线程 /任务类中接收一个闭锁,在线程执行完毕或者 finally 块里面调用 countDown()。
    需要等待其余线程执行完毕的主线程使用 await()等待闭锁打开就行了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5251 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 33ms UTC 06:00 PVG 14:00 LAX 23:00 JFK 02:00
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86