深入理解java虚拟机--线程安全与锁优化
- 面向过程编程思想和面向对象编程思想
- java语言中的线程安全
- 线程安全的实现方法
- 锁优化
面向过程编程思想和面向对象编程思想
面向过程编程思想:站在计算机的角度,以算法为核心,数据是客体,程序代码处理数据。
面向对象编程思想:站在现实世界的角度,数据和行为视为对象的一部分。
java语言中的线程安全
5类共享数据类别:1.不可变;2.绝对线程安全;3.相对线程安全;4.线程兼容;5.线程对立。
- 不可变(final)
不可变带来的安全性是最简单和最纯粹的。基本数据类型,final关键字修饰;共享数据是一个对象,对象的行为用final修饰。 - 绝对线程安全
定义:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。 - 相对线程安全
单独操作是线程安全的;需要额外的同步手段(特定顺序的连续调用)。
例子:Vector的get()、remove()、size()等方法;其他类如:HashTable等。 - 线程兼容
本身不是线程安全的;需要正确使用同步手段。
例子:Vector的get()、remove()、size()等方法;其他类如:ArrayList、HashMap等。 - 线程对立
无论是采取何种同步措施,都无法达到线程安全的。
例子:Thread类的suspend()和resume();其他类如:System类的setInt()、setOut()和runFinalizersOnExit()等。
线程安全的实现方法
3种实现方法:1.互斥同步;2.非阻塞同步;3.无同步方案。
- 互斥同步
也称阻塞同步;互斥是方法,同步是目的;互斥的实现方式主要有三种:临界区,互斥量,信号量。实现同步手段:synchronized和ReentrantLock。
-
synchronized:原生语法层面的互斥;关键字经过编译后在同步块前后形成monitorenter和monitorexit两个字节码指令,这两字字节码指令都需要一个reference类型的参数来指明要锁定和解锁的对象;如果synchronized修饰的实例方法则去取对应的对象实例,如果修饰的是类方法,则去取Class对象所为锁对象。有两点要注意的:1.synchronized同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的情况;2.同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入。
-
ReentrantLock:API层面的互斥;相对于synchronized来说有三个高级特性:1.等待可中断;2.可实现公平锁;3.锁可以绑定多个条件;
-
synchronized和ReentrantLock吞吐量比较
jdk1.5版本:ReentrantLock>synchronized;
jdk1.6后版本:synchronized、ReentrantLock两者持平; -
处理问题方式角度:互斥同步是悲观策略。
阻塞和唤醒一个线程,需要从用户态转换到核心态,消耗性能。
- 非阻塞同步
-
定义
基于冲突检测的乐观并发策略,先进行操作,如果没有其他线程的竞争共享数据,那操作就是成功了;如果共享数据有争用,产生了冲突,那就再采取其他的补充措施(最常见的补偿措施就是不断的重试,直到成功为止),这种乐观的策略很多实现都不需要把线程挂起。
-
处理问题方式角度:非阻塞同步是乐观策略。
-
CAS指令
内存位置(V)、旧的预期值(A)、新值(B);
CAS指令执行时,当且仅当V符合旧预期值A时,处理器用新值B更新V的值,否则它就不执行更新,但是无论是否更新了V的值,都会返回V的旧值,上述的处理过程是一个原子操作。CAS漏铜,ABA问题:
如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说它的值没有被其他线程改变过了吗?如果在这段期间它的值曾经被改成了B,后来又被改成了A,那CAS操作就会误认为它从来没有被改变过。
-
无同步方案
2类:1.可重入代码;2.线程本地存储。
-
可重入代码
3个特征:不依赖存储在堆上的数据和公用的系统资源、用到的状态量都是由参数传入、不调用非可重入的方法。 -
线程本地存储
例子:生产者-消费者:Web交互模型中的"一个请求对应一个服务器线程"的处理方式。
-
锁优化
5种锁优化:1.自旋锁和自适应自旋;2.锁消除;3.锁粗化;4.轻量级锁;5.偏向锁。
- 自旋锁和自适应自旋
- 自旋锁定义
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁。 - 自适应自旋定义
- 自旋的时间不在固定,由前一次在同一个锁上的自旋时间及锁的拥有者的状态决定。
- 原因
- 挂起线程和唤醒线程会消耗处理器时间
- 共享数据的锁定状态很多情况只会持续很短的时间
- 锁消除
- 定义
指在虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。
-
锁粗化
范围扩大,如果虚拟机探测到有这样的一串零碎的操作都是对同一个对象加锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部,这样只需要加锁一次就可以了。 -
轻量级锁
- 没有多线程的竞争前提下;减少传统的重量级所使用操作系统互斥量产生的性能消耗。
- Mark Word
- 偏向锁
- 消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。
- 不用做CAS操作。