实战Java高并发程序设计模式
作者:互联网
死锁、饥饿、活锁的概念。
并发级别:阻塞、饥饿、无障碍、无锁、无等待。
无障碍:是一种最弱的非阻塞调度。两个线程如果是无障碍的执行,那么他们不会因为临界区的问题导致一方被挂起。但是一旦检测到冲突,就应该进行回滚。
无锁:无锁的并行都是无障碍的,在无锁的情况下,所有的线程都能尝试对临界区进行访问,但是不同的是,无锁的迸发保证必然有一个线程能够在有限步内完成操作离开临界区。
while(!atomicVar.compareAndSet(localVar,localVar+1)){
localVar = atomicVar.get();
}
1
2
3
无等待:要求所有的线程都必须在有限步内完成,这样就不会引起饥饿问题。一种典型的无等待结构就是RCU(Read-Copy-Update)。它的基本思想是,对数据的读可以不加控制。因此,所有的读线程都是无等待的。但是在写数据的时候,先取得原始数据的副本,接着值修改副本数据,修改完成后,在合适的时机回写数据。
Amdah1定律
加速比定义: 加速比=优化前系统耗时/优化后系统耗时
Java的内存模型(JMM)
JMM的关键技术点都是围绕着多线程的原子性、可见性和有序性来建立的。
原子性
原子性是指一个操作是不可中断的。即使在多个线程一起执行的时候。一个操作一旦开始,就不会被其他线程干扰。
比如对于32位系统来说,对long型数据的读写不是原子性的。对int型数据读写是原子性的。
可见性
可见性是指当一个线程修改了某一个共享变量的值,其他线程能否立即知道这个修改。可见性问题是一个综合性问题。除了缓存优化或者硬件优化(有些内存读写可能会立即出发,而会先进入一个硬件队列等待)会导致可见性问题外,指令重排以及编辑器的优化,都有可能导致一个线程的修改避讳立即被其他线程察觉。
有序性
在并发时,程序的执行可能会出现乱序。给人直观的感觉是:写在前面的代码,会在后面执行。原因是程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。
假设线程A首先执行write()方法,接着线程B执行reader()方法,如果发生指令重排,那么线程B在代码第10行时,不一定看到a已经被赋值为1了。
指令重排是有一个基本的前提的,就是保证串行语义的一致性。指令重排不会使串行的语义逻辑发生问题。
那些指令不能重排:Happen-Before规则
虽然Java虚拟机和执行系统会对指令进行一定的重排,但是指令重排是有原则的,并非所有的指令都可以随便改变位置,一下是一些基本原则。
程序顺序原则:一个线程内保证语义的串行性
volatile规则:volatile变量的写,先发生于读,这保证了volatile变量的可见性
锁规则:解锁(unlock)必然发生于随后的加锁(lock)前
传递性:A先与B,B先于C,那么A必然先于C
线程的start()方法先于它的每个动作
线程的所有操作先于线程的终结(Thread.join())
线程的中断(interrupt())先于被中断线程的代码
对象的构造函数执行、结束先于finalize()方法
Java并行程序基础
继承Thread,继承Runable,继Callable,FutureTask等方式
线程状态图
2.2.1新建线程
2.2.2终止线程
线程的stop方法是被废弃而不推荐使用的,因为stop()方法太过暴力,强行把执行到一半的线程终止,可能会引发一些数据不一致的问题。
标签:执行,无锁,Java,指令,线程,重排,先于,设计模式,并发程序 来源: https://www.cnblogs.com/java1155/p/13308252.html