Java并发编程实战
王宝令
资深架构师
立即订阅
15151 人已学习
课程目录
已完结 50 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 你为什么需要学习并发编程?
免费
学习攻略 (1讲)
学习攻略 | 如何才能学好并发编程?
第一部分:并发理论基础 (13讲)
01 | 可见性、原子性和有序性问题:并发编程Bug的源头
02 | Java内存模型:看Java如何解决可见性和有序性问题
03 | 互斥锁(上):解决原子性问题
04 | 互斥锁(下):如何用一把锁保护多个资源?
05 | 一不小心就死锁了,怎么办?
06 | 用“等待-通知”机制优化循环等待
07 | 安全性、活跃性以及性能问题
08 | 管程:并发编程的万能钥匙
09 | Java线程(上):Java线程的生命周期
10 | Java线程(中):创建多少线程才是合适的?
11 | Java线程(下):为什么局部变量是线程安全的?
12 | 如何用面向对象思想写好并发程序?
13 | 理论基础模块热点问题答疑
第二部分:并发工具类 (14讲)
14 | Lock和Condition(上):隐藏在并发包中的管程
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
16 | Semaphore:如何快速实现一个限流器?
17 | ReadWriteLock:如何快速实现一个完备的缓存?
18 | StampedLock:有没有比读写锁更快的锁?
19 | CountDownLatch和CyclicBarrier:如何让多线程步调一致?
20 | 并发容器:都有哪些“坑”需要我们填?
21 | 原子类:无锁工具类的典范
22 | Executor与线程池:如何创建正确的线程池?
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
24 | CompletableFuture:异步编程没那么难
25 | CompletionService:如何批量执行异步任务?
26 | Fork/Join:单机版的MapReduce
27 | 并发工具类模块热点问题答疑
第三部分:并发设计模式 (10讲)
28 | Immutability模式:如何利用不变性解决并发问题?
29 | Copy-on-Write模式:不是延时策略的COW
30 | 线程本地存储模式:没有共享,就没有伤害
31 | Guarded Suspension模式:等待唤醒机制的规范实现
32 | Balking模式:再谈线程安全的单例模式
33 | Thread-Per-Message模式:最简单实用的分工方法
34 | Worker Thread模式:如何避免重复创建线程?
35 | 两阶段终止模式:如何优雅地终止线程?
36 | 生产者-消费者模式:用流水线思想提高效率
37 | 设计模式模块热点问题答疑
第四部分:案例分析 (4讲)
38 | 案例分析(一):高性能限流器Guava RateLimiter
39 | 案例分析(二):高性能网络应用框架Netty
40 | 案例分析(三):高性能队列Disruptor
41 | 案例分析(四):高性能数据库连接池HiKariCP
第五部分:其他并发模型 (4讲)
42 | Actor模型:面向对象原生的并发模型
43 | 软件事务内存:借鉴数据库的并发经验
44 | 协程:更轻量级的线程
45 | CSP模型:Golang的主力队员
结束语 (1讲)
结束语 | 十年之后,初心依旧
用户故事 (2讲)
用户来信 | 真好,面试考到这些并发编程,我都答对了!
3 个用户来信 | 打开一个新的并发世界
Java并发编程实战
登录|注册

34 | Worker Thread模式:如何避免重复创建线程?

王宝令 2019-05-16
上一篇文章中,我们介绍了一种最简单的分工模式——Thread-Per-Message 模式,对应到现实世界,其实就是委托代办。这种分工模式如果用 Java Thread 实现,频繁地创建、销毁线程非常影响性能,同时无限制地创建线程还可能导致 OOM,所以在 Java 领域使用场景就受限了。
要想有效避免线程的频繁创建、销毁以及 OOM 问题,就不得不提今天我们要细聊的,也是 Java 领域使用最多的 Worker Thread 模式。

Worker Thread 模式及其实现

Worker Thread 模式可以类比现实世界里车间的工作模式:车间里的工人,有活儿了,大家一起干,没活儿了就聊聊天等着。你可以参考下面的示意图来理解,Worker Thread 模式中 Worker Thread 对应到现实世界里,其实指的就是车间里的工人。不过这里需要注意的是,车间里的工人数量往往是确定的。
车间工作示意图
那在编程领域该如何模拟车间的这种工作模式呢?或者说如何去实现 Worker Thread 模式呢?通过上面的图,你很容易就能想到用阻塞队列做任务池,然后创建固定数量的线程消费阻塞队列中的任务。其实你仔细想会发现,这个方案就是 Java 语言提供的线程池。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java并发编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(24)

  • vector
    工厂里只有一个工人,他的工作就是同步的等待工厂里其他人给他提供东西,然而并没有其他人,他将等到天荒地老,海枯石烂~

    作者回复: 比喻很形象👍

    2019-05-16
    36
  • 曾轼麟
    EagerThreadPool 老师这个线程池可以避免死锁的情况,死锁的时候会自动撑大

    作者回复: 👍👍

    2019-05-23
    11
  • linqw
    newSingleThreadExecutor线程池只有单个线程,先将外部线程提交给线程池,外部线程等待内部线程执行完成,但由于线程池只有单线程,导致内部线程一直没有执行的机会,相当于内部线程需要线程池的资源,外部线程需要内部线程的结果,导致死锁。
    2019-05-26
    8
  • zero
    感觉这程序会调用栈内存溢出,这段代码相当于无限的递归调用啊。不知道理解的对不对,请老师指点。

    作者回复: 不是递归,但会死锁

    2019-05-18
    5
  • 佑儿
    原始的workerThread模式包含三种角色:工人、传送带、产品,
    传送带中维护一个productionsQueue以及最大的产品数量(为了防止产品无限积压),
    在传送带初始化时,创建了若干个worker(线程),worker不断从传送带取产品进行加工,
    当传送带中无产品时,worker线程被挂起等待唤醒,当有新的产品加入到传送带中时,挂起的worker会被唤醒,取产品加工。
    当上游线程Thread往传送带中加入产品时,如果productionsQueue到达最大产品数量时,Thread会被挂起。
    当有worker线程取出产品后,会唤醒阻塞的线程Thread(当然这里也有可能唤醒worker)
    线程池只是workerThread的一种实现,那么线程池中创建的Thread就是工人,线程池本身就是传送带,产品就是提交到线程池中的Runnable,
    而在线程池中的阻塞队列就相当于productionsQueue,请问老师,我这样理解是否正确?

     
    2019-05-17
    5
  • 木刻
    希望老师能开一栏专门讲一讲Linux下多线程并发情况下程序性能的排查和调优。谢谢老师

    作者回复: 好累😂

    2019-05-17
    4
  • ack
    老师,请教个问题,线程死锁那个代码,是活锁吗,思考题我也认为是活锁

    作者回复: 我觉得是死锁,活锁有释放再获取的过程

    2019-05-16
    2
  • 晓杰
    线程池里面的最大线程数只有一个,无法做到异步
    2019-05-16
    1
  • Planeswalker23
    首先它是一个单线程的线程池,第一次submit的任务是获取“给pool线程池设置的第二个submit任务的返回值”,然后输出。然而因为是单线程池,永远也等不到第二个线程任务返回QQ。第一个线程任务依赖于第二个任务,导致死锁。呼应本章主题:线程池分工。
    2019-12-06
  • Mr_杨
    老师请教个问题,如果不同业务用不同线程池,保证不了线程数量,会带来并发线程过大,如何控制频繁上下文切换的问题

    作者回复: 无解,必须控制线程数量。

    2019-11-07
  • Sharry
    这个 WorkerThread 任务分配模式与生产者消费者很像啊
    2019-09-30
  • Sharry
    只有一个线程, 会阻塞在 get 方法上, 直到永久
    2019-09-30
  • DFighting
    执行代码后查看线程栈会发现pool-1-thread-1一直处于waiting状态,因为外层的pool.submit等待内层pool.submit,内层又在等待外层submit的释放,简直是相互伤害到地老天荒啊。解决方案有两种:
    1、最简单就是把线程个数从1个改到2个,然后代码运行就会成功打印出来QQ
    2、真正要解决的还是需要针对有依赖关系的代码分配不同的线程池,只有真正无关的逻辑才能共用一个线程池。
    第一次发现原来线程池之中还有deadlock的问题存在,其实说白了,线程池只是解决了频繁线程创建和销毁的代价问题,关于并发所带来的的问题,它是一点力都没出的,甚至某些时候还带来了一些问题(比如threadlocal)。
    2019-09-29
  • nonohony
    外部线程会由于内部线程submit.get而阻塞,占有single线程池的唯一worker资源,从而导致内部线程永远无法执行,形成活锁。解法可以拆分为两个线程池。
    2019-05-22
  • 扬~
    可以出个线程池异常处理的方案吗
    2019-05-18
  • 佑儿
    有问题,singlepool中只有一个线程池,future.get方法阻塞当前线程,导致打印qq的线程没有机会执行,会根据丢弃策略进行不同的操作。
    2019-05-16
  • 线程池只有一个线程,在任务执行的时候不能有再多的线程去处理提交的任务。
    2019-05-16
  • 周治慧
    两个线程共用一个线程池,当线程池中只有一个线程时,第二个线程是拿那不到线程的
    2019-05-16
  • 孙志强
    死锁
    2019-05-16
  • 密码123456
    跟今天的例子好像。一个线程池,却提交2个任务,其中一个线程等待另外一个线程
    2019-05-16
收起评论
24
返回
顶部