还不懂 Java 中的多线程?


还不懂 Java 中的多线程?

文章插图
 
作者 | 纳达丶无忌原文 | jianshu.com/p/40d4c7aebd66
前言
由于此订阅号换了个皮肤,导致部分用户接受文章不及时 。可以打开本订阅号,选择置顶(标星)公众号,重磅干货,第一时间送达!
正文
如果对什么是线程、什么是进程仍存有疑惑,请先 google 之,因为这两个概念不在本文的范围之内 。
用多线程只有一个目的,那就是更好的利用 CPU 的资源,因为所有的多线程代码都可以用单线程来实现 。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型” 。
很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会 。
  • 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
  • 并行与并发:
  • 并行:多个 CPU 实例或者多台机器同时执行一段处理逻辑,是真正的同时 。
  • 并发:通过 CPU 调度算法,让用户看上去同时执行,实际上从 CPU 操作层面不是真正的同时 。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用 TPS 或者 QPS 来反应这个系统的处理能力 。

还不懂 Java 中的多线程?

文章插图
并发与并行
  • 线程安全:经常用来描绘一段代码 。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果 。这个时候使用多线程,我们只需要关注系统的内存,CPU 是不是够用即可 。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:
void transferMoney(User from, User to, float amount){ to.setMoney(to.getBalance + amount); from.setMoney(from.getBalance - amount);}
  • 同步:JAVA 中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确 。如上面的代码简单加入 @synchronized 关键字 。在保证结果准确的同时,提高性能,才是优秀的程序 。线程安全的优先级高于性能 。

  •  
好了,让我们开始吧 。我准备分成几部分来总结涉及到多线程的内容:
1. 扎好马步:线程的状态
2. 内功心法:每个对象都有的方法(机制)
3. 太祖长拳:基本线程类
4. 九阴真经:高级多线程控制类
扎好马步:线程的状态
先来两张图:
还不懂 Java 中的多线程?

文章插图
线程状态
还不懂 Java 中的多线程?

文章插图
线程状态转换
各种状态一目了然,值得一提的是 "Blocked" 和 "Waiting" 这两个状态的区别:
  • 线程在 Running 的过程中可能会遇到阻塞 (Blocked) 情况
    对 Running 状态的线程加同步锁 (Synchronized) 使其进入 (lock blocked pool),同步锁被释放进入可运行状 (Runnable) 。从 jdk 源码注释来看,blocked 指的是对 monitor 的等待(可以参考下文的图)即该线程位于等待区 。
  • 线程在 Running 的过程中可能会遇到等待(Waiting)情况线程可以主动调用 object.wait 或者 sleep,或者 join(join内部调用的是 sleep,所以可看成 sleep 的一种)进入 。从 jdk 源码注释来看,Waiting 是等待另一个线程完成某一个操作,如 join 等待另一个完成执行,object.wait 等待object.notify 方法执行 。
Waiting 状态和Blocked 状态有点费解,我个人的理解是:Blocked 其实也是一种 wait,等待的是 monitor,但是和Waiting 状态不一样,举个例子,有三个线程进入了同步块,其中两个调用了 object.wait,进入了 Waiting 状态,这时第三个调用了 object.notifyAll,这时候前两个线程就一个转移到了 Runnable,一个转移到了 Blocked 。
从下文的 monitor 结构图来区别:每个 Monitor 在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set” 和 “Wait Set” 里面等候 。在 “Entry Set” 中等待的线程状态 Blocked,从 jstack 的dump 中来看是 “Waiting for monitor entry”,而在 “Wait Set” 中等待的线程状态是 Waiting,表现在 jstack 的 dump 中是 “in Object.wait” 。


推荐阅读