Java 如何实现异步更新数据库呢? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chunrong918
V2EX    Java

Java 如何实现异步更新数据库呢?

  •  
  •   chunrong918 2017-10-07 11:01:03 +08:00 9972 次点击
    这是一个创建于 2928 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题:

    比如程序要执行以下步骤:

    A:往单例类的 list 属性中写数据( list 的 size 到达 100 的时候,把 list 保存到数据中,同时清空 list )

    B:执行业务,return true

    现在希望是程序进来的时候,马上执行 B,异步去执行 A,请问如何实现呢?

    谢谢大神们!

    27 条回复    2017-10-13 18:08:31 +08:00
    tinyuu
        1
    tinyuu  
       2017-10-07 11:02:44 +08:00
    线程啊
    chunrong918
        2
    chunrong918  
    OP
       2017-10-07 11:06:17 +08:00
    @tinyuu

    你的意思是每次进来的时候 new 一个 thread,去执行 A 操作?

    用同步锁锁住单例累,执行 A ?


    有没有办法用并发包?
    hwding
        3
    hwding  
       2017-10-07 11:08:34 +08:00 via iPhone
    带优先级的阻塞队列?就像消息那样。
    chunrong918
        4
    chunrong918  
    OP
       2017-10-07 11:09:48 +08:00
    @tinyuu 往 list 属性中写和清空,应该算一个 pv 操作?
    chunrong918
        5
    chunrong918  
    OP
       2017-10-07 11:11:11 +08:00
    @hwding 有参考资料吗?能大概讲一下吗?因为我以前没接触过
    hwding
        6
    hwding  
       2017-10-07 11:16:16 +08:00 via iPhone
    @chunrong918 大概是多线程和并发以及异步这一块的吧,具体哪本书我也说不上来。也可以选用一些库。
    BBCCBB
        7
    BBCCBB  
       2017-10-07 11:24:18 +08:00
    要用并发库就把 list 改为 blockingqueue 或者 concurrentqueue 之类的,然后就是线程了
    v2orz
        8
    v2orz  
       2017-10-07 11:28:11 +08:00
    不要用单例类的 list 而是用队列
    往线程池提交任务也行,其实也是内部的队列
    azygote
        9
    azygote  
       2017-10-07 11:29:57 +08:00 via iPhone
    用 redis 队列
    hantsy
        10
    hantsy  
       2017-10-07 11:38:55 +08:00
    用 Spring 一个 @Async 就可以了。
    更好的方法用一些 Messaging Broker,如 RabbitMQ 去处理。
    tinyuu
        11
    tinyuu  
       2017-10-07 11:42:33 +08:00
    @chunrong918 开始就创建一个线程 ,不要用 list。用 queue ;
    chocotan
        12
    chocotan  
       2017-10-07 11:56:01 +08:00
    貌似可以用 rxjava 的 buffer
    sagaxu
        13
    sagaxu  
       2017-10-07 13:55:07 +08:00   1
    在初始化 bean 的时候创建一个 ConcurrentLinkedQueue 和一个 AtomicInteger 计数器,还要注入一个 ThreadPoolExecutor。

    A 往 queue 里塞数据的时候计数器加 1,如果满 100,把计数器减 100,并且往 ThreadPoolExecutor 里扔一个消费 100 个数据的 task,这里减计数器和扔 task 要做好同步,用 double check 加锁简单同步下就可以了

    if (counter >= 100) {
    synchronized (this) {
    if (counter >= 100) {
    }
    }
    }

    除了每满 100 个,还可以控制下时间,比如上一次写入 db 有 5 分钟了,那么不管当前满没满 100 也要把 queue 里的数据写入 db,光靠数量控制是不够的。


    需要特别注意的是,不要在 controller 里创建线程,那是个不好的习惯,容器里的线程是被托管的,你在托管的线程里创建自己的线程,会带来潜在问题。
    yidinghe
        14
    yidinghe  
       2017-10-07 14:12:44 +08:00 via Android
    1、肯定是用另外的线程;
    2、如果并发量很大,就用线程池来控制负载。
    loveCoding
        15
    loveCoding  
       2017-10-07 14:28:13 +08:00
    队列+线程池吧
    movistar
        16
    movistar  
       2017-10-07 15:04:00 +08:00
    经典的生产者消费者模型
    另外是不是真的到 100 个要 flush 一次呢,这个是业务场景?
    一般来说异步模式的消费者会这么做:
    提供一个最长 flush time,以及一个队列最长长度,如果到这个时间阈值,队列还没满,那么直接清空队列进行消费
    如果还没到时间阈值,队列满了,就直接清空队列进行消费
    需要考虑 13 楼说的,消费者过慢的问题,提供多个消费者,在多个线程中进行消费,避免 block
    有很多实现方式,可以参考一下 ElasticSearch 的 BulkProcessor
    相对比较完善比较完整,如果不需要这么复杂的逻辑,可以简化一下
    https://github.com/elastic/elasticsearch/blob/c47f24d4061f51c8e831d030443afa90d73f681c/core/src/main/java/org/elasticsearch/action/bulk/BulkRequestHandler.java
    sorra
        17
    sorra  
       2017-10-07 17:45:05 +08:00
    在 Executors 里选一种线程池,往里面提交任务就可以,注意正确选择队列排满时的行为(阻塞或拒绝)。
    生产环境推荐用专门的消息队列来做,不会丢失任务。
    vela
        18
    vela  
       2017-10-07 19:15:23 +08:00
    https://github.com/PhantomThief/buffer-trigger
    之前抽象过一个这样的组件,不过是 Java8 only 的,仅供参考和借鉴。
    sudoz
        19
    sudoz  
       2017-10-07 21:04:45 +08:00
    线程池 MQ
    chunrong918
        20
    chunrong918  
    OP
       2017-10-07 23:20:28 +08:00
    @hwding 这边不需要考虑优先级,感觉阻塞队列加线程就可以解决了
    chunrong918
        21
    chunrong918  
    OP
       2017-10-07 23:21:05 +08:00
    @BBCCBB 是啊,阻塞队列加线程就可以解决了
    chunrong918
        22
    chunrong918  
    OP
       2017-10-07 23:52:04 +08:00
    @movistar 确实向你说的,考虑时间和队列长度问题,但是 elasticsearch 的那个类源码看不太懂,有点复杂呢
    chunrong918
        23
    chunrong918  
    OP
       2017-10-08 00:06:29 +08:00
    @sagaxu 好办法,这边的控制时间,定时去更新是使用 java 的定时器 timer 还是 crontab 啊?
    chunrong918
        24
    chunrong918  
    OP
       2017-10-08 00:07:09 +08:00
    @sagaxu 有相关代码可以参考一下吗
    codeyung
        25
    codeyung  
       2017-10-08 00:09:04 +08:00 via iPhone
    异步 或者 队列线程 可以看下相关文档 和 GitHub
    cxbig
        26
    cxbig  
       2017-10-08 00:42:39 +08:00
    一般我们都是用队列( Queue )做平衡
    upupxjg
        27
    upupxjg  
       2017-10-13 18:08:31 +08:00
    多线程,不是说 A、B 搞两个线程,而是把 A 写库这个事儿扔到线程池里去,List 攒够 100 (必须是 100 么?多一点都不行?) A 加锁,把这个 List 扔给线程池,再 new 一个 list 解锁。打完收工
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4364 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 33ms UTC 05:34 PVG 13:34 LAX 22:34 JFK 01:34
    Do have faith in what you're doing.
    ubao 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