目录
1 概述
2 硬件效率与一致性
3 Java 内存模型
3.1 主内存和工作内存
3.2 内存间的交互操作
3.3 volatile 变量的特殊规则
3.4 原子性、可见性、有序性
3.5 先行发生原则
4 Java 与线程
4.1 线程的实现
4.2 Java线程的实现
4.3 Java线程调度
4.4 状态转换
参考:
<>加了一层读写速度尽可能接近处理器从运算速度的高速缓存。
<>代码乱序执行优化
高速缓存的引入带来的新的问题:缓存一致性多处理器系统中,每个处理器有自己的高速缓存,同时共享一个主内存。汇编代码中的lock 前缀操作。他的作用是死的本CPU的Cache写入了内存,也会引起别的CPU或者内核无效化其Cache,相当于是对volatile 修饰的变量进行了一次 store 和write 操作。所以 volatile 变量的修改对其他CPU立即可见。
【特性2】禁止指令重排序优化汇编代码中的lock 前缀操作。相当于一个内存屏障(重排时是不能把后面的指令排序到内存屏障之前的位置)。所以lock 前缀操作指令把修改同步到内存时,以为这之前的所有操作已经执行完毕。形成了“指令重排无法越过内存屏障”的效果。
volatile 变量只能保证可见性,不符合以下情况的话,仍然要通过加锁保证原子性<1>运算结果不依赖变量的当前值,或者确保只有单一线程修改变量的值
<2>变量不需要与其他状态的变量共同参与不变约束
Java内存模型对于 volatile 变量定义的特殊规则:<1>使用变量前必须从主内存刷新最新的值
<2>每次修改变量之后必须立即同步回主内存
<3>volatile 修饰的变量不会被指令重排优化
<1> 程序次序规则:线程中的控制流顺序
<2>管程锁定规则:一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。
<3> volatile 变量规则:一个 volatile 变量的写操作先行发生于后面对这个变量的读操作
<4> 线程启动规则:Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。
<5> 线程终止规则:Thread 对象的的所有操作都先行发生于此线程的终止检测。
<6> 线程中断规则:对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生
<7> 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。
<8> 传递性:如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。
时间先后顺序与先行发生原则直接按基本没有太大关系。<1> 使用内核线程实现
>程序使用内核线程的一种高级接口--轻量级线程(LWP,也就是我们通常所讲的线程)
>轻量级线程与内核线程之间是1:1的。
>局限性:
a)各种现场的操作都需要系统调用,系统调用的代价较高,会有用户态和内核态的切换;b)消耗一定的内核资源,数量有限。
<2> 使用用户线程实现
>用户线程的建立、同步、销毁和调度完全在用户态完成.;可以支持规模更大的线程数量。
>进程与用户线程之间1:N
<3> 使用用户线程加轻量级进程混合实现
>操作系统提供支持的轻量级进程作为用户线程和内核线程之间的桥梁;可以使用内核提供的线程调度功能及处理器映射;用户线程的系统调用通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。
>用户线程和轻量级进程N:M;
<1> 协同式调度
好处:简单、切换操作对线程可知,不存在线程同步问题。
坏处:线程执行时间不可控。
<2>抢占式调度
线程的执行时间是系统可控的。-------Java采用的线程调度方式。
Java 设置有现成优先级。
线程优先级不太靠谱:1)在一些平台上不同的优先级可能变的相同;2)优先级可能会被系统自行改变(window中的优先级推进器等。)
Java有5种线程状态:
新建创建,未执行
运行可能在运行,可能在等待CPU 分配执行时间
包括操作系统线程状态中的Running 和 Ready。
无限期等待不会被分配CPU时间。需要其他线程显示唤醒。
以下方法会让线程无限期等待:
进入方法退出方法没有设置 Timeout 参数的 Object.wait() 方法Object.notify() / Object.notifyAll()没有设置 Timeout 参数的 Thread.join() 方法被调用的线程执行完毕LockSupport.park() 方法LockSupport.unpark(Thread) 限期等待不会被分配CPU时间。;不需要其他线程显示唤醒。
以下方法会让线程限期等待:
进入方法退出方法Thread.sleep() 方法时间结束设置了 Timeout 参数的 Object.wait() 方法时间结束 / Object.notify() / Object.notifyAll()设置了 Timeout 参数的 Thread.join() 方法时间结束 / 被调用的线程执行完毕LockSupport.parkNanos() 方法LockSupport.unpark(Thread)LockSupport.parkUntil() 方法LockSupport.unpark(Thread) 阻塞阻塞和等待的区别是:阻塞状态在等待获取到一个排他锁。这个事件将在一个线程放弃这个锁的时候发生;等待状态这是在等待一段时间或者唤醒动作的发生。
结束终止线程状态。
https://cyc2018.github.io/CS-Notes/#/notes/Java 并发?id=一、线程状态转换
《深入理解Java虚拟机》