《深入浅出Java多线程》系列文章学习

第一章 基础篇

1.进程是操作系统进行资源分配的基本单位,而线程是操作系统进行调度的基本单位,即CPU分配时间的单位。

2.Java中的优先级来说不是特别的可靠,Java程序中对线程所设置的优先级只是给操作系统一个建议,操作系统不一定会采纳。而真正的调用顺序,是由操作系统的线程调度算法决定的。

3.反复调用同一个线程的start()方法是否可行?假如一个线程执行完毕(此时处于TERMINATED状态),再次调用这个线程的start()方法是否可行?

  答:不可行。重复调用start()方法会抛出IllegalThreadStateException异常。这部分内容可参考代码

// 省略相关package和import信息
public
class ThreadStateDemo { public static void main(String[] args) { startTwice(); } /** * 调用start方法两次 * * 查看java.lang.Thread#start()源码可以发现,其内部有一个变量threadStatus * 若该值不为0,会直接抛出java.lang.IllegalThreadStateException异常 * 下方demo,第一次调用start()方法,变量threadStatus值为0 * 第二次调用start()方法,变量threadStatus的值不为0,直接抛异常 */ private static void startTwice() { Thread thread = new Thread(() -> {}); thread.start(); // 第二次调用时,会抛出java.lang.IllegalThreadStateException异常 thread.start(); } }

 

4.Java线程的6个状态

// Thread.State 源码
public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

线程状态转换见下图:

 

 5.关于Object.wait()和Thread.sleep(long):前者会释放锁,后者不释放锁。二者都释放cpu资源。

第二章 原理篇

Java内存模型

我们知道,运行时数据区中,栈是线程私有的,堆是所有线程共享的。

也就是说在栈中的变量(局部变量、方法定义参数、异常处理器参数)不会在线程之间共享,也就不会有内存可见性的问题,也不受内存模型的影响。

所以,内存可见性是针对的堆中的共享变量。

既然堆是共享的,为什么会有内存可见性的相关问题?

==> 现代计算机为了高效,往往会在高速缓存区中缓存共享变量,因为cpu访问缓存区比访问内存快得多。

线程之间的共享变量存在主内存中,每个线程都有一个私有的本地内存,存储了该线程已读、写共享变量的副本。本地内存是Java内存模型的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器等。

Java线程之间的通信由Java内存模型(简称JMM)控制,从抽象的角度来说,JMM定义了线程和主内存之间的抽象关系。JMM的抽象示意图如图所示:

 (ps:这个图很重要,要理解好Java内存模型,并能够将这个图画出来)

从图中可以看出:

  1. 所有的共享变量都存在主内存中。
  2. 每个线程都保存了一份该线程使用到的共享变量的副本。
  3. 如果线程A与线程B之间要通信的话,必须经历下面2个步骤:
    1. 线程A将本地内存A中更新过的共享变量刷新到主内存中去。
    2. 线程B到主内存中去读取线程A之前已经更新过的共享变量。

所以,线程A无法直接访问线程B的工作内存,线程间通信必须经过主内存。

注意,根据JMM的规定,线程对共享变量的所有操作都必须在自己的本地内存中进行,不能直接从主内存中读取。

所以线程B并不是直接去主内存中读取共享变量的值,而是先在本地内存B中找到这个共享变量,发现这个共享变量已经被更新了,然后本地内存B去主内存中读取这个共享变量的新值,并拷贝到本地内存B中,最后线程B再读取本地内存B中的新值。

那么怎么知道这个共享变量的被其他线程更新了呢?这就是JMM的功劳了,也是JMM存在的必要性之一。JMM通过控制主内存与每个线程的本地内存之间的交互,来提供内存可见性保证。

Java中的volatile关键字可以保证多线程操作共享变量的可见性以及禁止指令重排序,synchronized关键字不仅保证可见性,同时也保证了原子性(互斥性)。在更底层,JMM通过内存屏障来实现内存的可见性以及禁止重排序。为了程序员的方便理解,提出了happens-before,它更加的简单易懂,从而避免了程序员为了理解内存可见性而去学习复杂的重排序规则以及这些规则的具体实现方法。这里涉及到的所有内容后面都会有专门的章节介绍。

 

参考内容

http://concurrent.redspider.group/RedSpider.html

posted @ 2021-09-05 15:04  chan_xm  阅读(61)  评论(0)    收藏  举报