可见性、原子性和有序性
计算机 CPU、内存、I/O 设备存在很大的速度差异
为了均衡 CPU 和内存速度的差异 CPU 会优先使用内置缓存,这样会导致可见性问题
操作系统分时复用 CPU 来均衡 CPU 与 I/O 设备速度差异,这样线程切换会导致原子性问题
- 可见性
- 原子性
- 有序性
volatile 关键字
volatile 关键字是用于线程间通讯的特殊字段。每次读 volatile 字段都会看到其它线程写入该字段的最新值;实际上,程序员之所以要定义volatile字段是因为在某些情况下由于缓存和重排序所看到的陈旧的变量值是不可接受的。编译器和运行时禁止在寄存器里面分配它们。它们还必须保证,在它们写好之后,它们被从缓冲区刷新到主存中,因此,它们立即能够对其他线程可见
synchronized 关键字
Happens-Before 规则
Java 语言规范定义了 Java 内存模型(Java Memory Model,JMM)核心内容是 Happens - before 规则
Happens - before 规则表达的含义是:前面一个操作的结果对后续操作是可见的,有如下几项
- 管程中的锁规则
对一个锁的解锁 happens-before 后续对这个锁的加锁,使用 synchronized 关键字时,进入同步代码块之
前会自动加锁,在代码块执行完成时会自动释放锁,使用 Lock 接口时,进入同步代码块之前需要先调用 lock
方法,退出同步代码块时需要调用 unlock 方法 - volatile 变量规则
对 volatile 变量的写入 happens-before 接下来对该变量的读取操作 - 线程 start 规则
主线程 A 启动子线程 B,子线程 B 可以看到主线程在启动子线程 B 之前的操作 - 线程 join 规则
主线程 A 等待子线程 B 完成,子线程 B 完成后,主线程能够看到子线程的操作 - 任何对象的默认初始化 happens-before 该程序的任何其他操作
- 线程对另一个线程的中断 happens-before 被中断线程检测到该中断
- 对象构造方法的结束 happens-before 这个对象的销毁
信号量和管程的对比
信号量是一个整数变量(S)代表可共享资源的数量,管程是一种程序结构每次只允许一个线程访问临界区
The value of semaphore can be modified by wait() and signal() operation only. On the other hand, a monitor has the shared variables and the procedures only through which shared variables can be accessed by the processes.
信号量的值只可以被 wait() 和 signal() 修改,
In Semaphore when a process wants to access shared resources the process performs wait() operation and block the resources and when it release the resources it performs signal() operation. In monitors when a process needs to access shared resources, it has to access them through procedures in monitor.
当程序访问共享资源时,需要先调用信号量的 wait() 操作,然后占有资源直到调用 signal() 操作后才释放资源,在管程只能通过调用监视器的程序
管程具有条件变量,信号量没有条件变量
信号量
信号量概念是荷兰计算机科学家迪杰斯特拉于 1963 年提出和发明的
JDK 1.5 版本提供了信号量的实现 java.util.concurrent.Semaphore
信号量可以用来控制对共享资源的互斥访问,synchronized 关键字代表的管程与信号量可以实现同等效果
管程
管程是一种通用的同步原语,管程概念出现前多个线程对共享资源并发访问问题只能用信号量解决
1972 年由两位计算机科学家提出了管程的构思并最终实现,Java 语言提供了两种管程实现方案
一种是 Java 语言内置的 synchronized 关键字另一种是 JDK 1.5 提供的 Lock&Condition 接口
Lock 接口用于实现互斥访问,Condition 接口用来解决同步问题
相对于 synchronized 关键字,这两个接口可以做到响应中断,支持获取锁超时,支持非阻塞获取锁
等待 - 通知机制
等待 - 通知机制是一种线程间协作的方式,通常我们使用轮询的方式来等待每个状态,如果轮询操作很耗时或者并发冲突量大很消耗 CPU,这个时候可以使用等待 - 通知机制替换轮询等待。
- synchronized 关键字配合 wait()、notify()、notifyAll() 可以实现等待 - 通知机制
- Lock&Condition 配合 await()、signal()、signalAll() 可以实现等待 - 通知机制
wait()、notify()、notifyAll() 是 Object 类的方法,其他类都继承了 Object 类所以都有这三个方法
await()、signal()、signalAll() 是 Condition 接口提供的方法
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。