From:Java并发编程的艺术
- 目录
BiBi - 并发编程 -0- 开篇
BiBi - 并发编程 -1- 挑战
BiBi - 并发编程 -2- volatile
BiBi - 并发编程 -3- 锁
BiBi - 并发编程 -4- 原子操作
BiBi - 并发编程 -5- Java内存模型
BiBi - 并发编程 -6- final关键字
BiBi - 并发编程 -7- DCL
BiBi - 并发编程 -8- 线程
BiBi - 并发编程 -9- ReentrantLock
BiBi - 并发编程 -10- 队列同步器
BiBi - 并发编程 -11- 并发容器
BiBi - 并发编程 -12- Fork/Join框架
BiBi - 并发编程 -13- 并发工具类
BiBi - 并发编程 -14- 线程池
BiBi - 并发编程 -15- Executor框架
线程通信的两种机制:共享内存【隐式】和消息传递【显示】,Java的并发采用共享内存模型,线程A向线程B发送消息,这个通信过程必须经过主存。
指令重排序
编译器和处理器为了优化程序性能而对指令序列进行重新排序,但不会对存在数据依赖关系的操作做重排序。
1)编译器重排序
2)处理器重排序
包括:指令级并行的重排序【将多条指令重叠执行】和内存级重排序。
对于处理器重排序,在Java编译器生成指令序列时,通过插入特定类型的内存屏障【Memory Barriers / Memory Fence】指令来禁止重排序,从而保证内存的可见性。现代的处理器都采用写缓冲区向内存写入数据。
StoreLoad Barriers
StoreLoad Barriers屏障是一个【全能型】屏障,并且大部分处理器都支持,它会把写缓冲区中的数据全部刷新到内存中。开销很高。
happens-before
使用happens-before来阐述操作之间的内存可见性。
happens-before规则:
1)一个锁的解锁,先于随后对这个锁的加锁
2)volatile变量的写,先于任意后续对这个变量的的读【volatile的本质】
3)传递性:A先于B,B先于C,则A先于C
happens-before提示:
具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行,仅仅要求前一个操作执行的结果对后一个操作可见,并且前一个操作按顺序排在第二个操作之前。
如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM保证a操作对b操作可见。
控制依赖重排序
if ( flag ) { // 1
int i = age + 10; // 2
}
操作1和操作2存在控制依赖关系,他们重排序后,处理器可以提前读取并计算age + 10
,然后把计算结果临时保存到一个重排序缓冲的硬件缓存中。当操作1的条件判断为真时,再把计算结果写入到变量i中。
顺序一致性
顺序一致性模型简介:
1)顺序一致性模型保证单线程内的操作会按程序的顺序执行,而JMM不保证单线程内的操作会按照程序的顺序执行【会重排序】。
2)顺序一致性模型保证所有线程只能看到一致的操作执行顺序,而JMM不保证。
3)JMM不保证对64位的long型和double型变量的写操作具有原子性【JDK1.5之后读具有原子性】,而顺序一致性模型保证对所有的内存读/写操作都具有原子性。
第3)点与处理器的总线机制有关。总线同步,在一个处理器执行总线事物期间,总线会禁止其他的处理器和I/O设备的读写操作。即总线工作机制:把所有处理器对内存的访问以串行化的方式来执行,在任意时间点,最多只能有一个处理器可以访问内存。确保单个总线事物之中的读写操作具有原子性。
在32位处理器上,JVM可能把一个64位long/double型变量的写操作拆分成两个32位的写操作来执行,这两个32位的写操作可能会被分配到不同的总线事物中执行,因此不具有原子性。【volatile类型的long/double读写都具有原子性】
在单线程中重排序不会改变执行结果,但在多线程中会影响执行结果。
临界区【锁保护区】内的代码可以重排序。
网友评论