1.概述数组
在要点提炼| 理解JVM以内存模型&线程中主要介绍了虚拟机如何实现『并发』,如今的关注点是虚拟机如何实现『高效』。安全
2.线程安全多线程
在实现高效以前,首先须要保证并发的正确性,所以本节先介绍线程安全。并发
a.定义:当多个线程访问一个对象时,若是不用考虑这些线程在运行时环境下的调度和交替执行,也不须要进行额外的同步,或者在调用方进行任何其余的协调操做,调用这个对象的行为均可以得到正确的结果,那这个对象是线程安全的。函数
要求线程安全的代码都必须具有一个特征: 代码自己封装了全部必要的正确性保障手段(如互斥同步等),令调用者无须关心多线程的问题,更无须本身采起任何措施来保证多线程的正确调用。布局
b.分类:按照线程安全的程度由强至弱分红五类post
final
关键字修饰;final
。Vector
、HashTable
、Collections#synchronizedCollection()
包装的集合...ArrayList
和HashMap
...c.线程安全的实现性能
可分红两大手段,本篇重点在虚拟机自己测试
- 经过代码编写实现线程安全
- 经过虚拟机自己实现同步与锁
①互斥同步(Mutual Exclusion&Synchronization)优化
互斥是因,同步是果;互斥是方法,同步是目的。
synchronized
关键字:
monitorenter
和monitorexit
这两个字节码指令,并经过一个reference
类型的参数来指明要锁定和解锁的对象。若明确指定了对象参数,则取该对象的reference
;不然,会根据synchronized
修饰的是实例方法仍是类方法去取对应的对象实例或Class对象来做为锁对象。monitorenter
指令时先要尝试获取对象的锁。若该对象没被锁定或者已被当前线程获取,那么锁计数器+1;而在执行monitorexit
指令时,锁计数器-1;当锁计数器=0时,锁就被释放;若获取对象锁失败,那当前线程会一直被阻塞等待,直到对象锁被另一个线程释放为止。synchronized
同步块对同一条线程来讲是可重入的,不会出现自我锁死的问题;还有,同步块在已进入的线程执行完以前,会阻塞后面其余线程的进入。ReentrantLock
:
synchronized
很类似,且均可重入。synchronized
的不一样:
synchronized
是非公平的,即在锁被释放时,任何一个等待锁的线程都有机会得到锁。ReentrantLock
默认状况下也是非公平的,但能够经过带布尔值的构造函数改用公平锁。ReentrantLock
对象能够经过屡次调用newCondition()
同时绑定多个Condition
对象。而在synchronized
中,锁对象的wait()
和notify()
或notifyAl()
只能实现一个隐含的条件,若要和多于一个的条件关联不得不额外地添加一个锁。synchronized
能实现需求的状况下,优先考虑使用它来进行同步。下两张图是二者在不一样处理器上的吞吐量对比。②非阻塞同步(Non-Blocking Synchronization):
③无同步方案
知足可重入性的代码必定是线程安全的,反之,知足线程安全的代码不必定是可重入的。
ThreadLocal
类可实现线程本地存储的功能:每一个线程的Thread
对象中都有一个ThreadLocalMap对象,它存储了一组以ThreadLocal.threadLocalHashCode为key、以本地线程变量为value的键值对,而ThreadLocal对象就是当前线程的ThreadLocalMap的访问入口,也就包含了一个独一无二的threadLocalHashCode值,经过这个值就能够在线程键值值对中找回对应的本地线程变量。3.锁优化
解决并发的正确性以后,为了能在线程之间更『高效』地共享数据、解决竞争问题、提升程序的执行效率,下面介绍五种锁优化技术。
a.适应性自旋(Adaptive Spinning)
b.锁消除(Lock Elimination)
c.锁粗化(Lock Coarsening)
通常状况下,会将同步块的做用范围限制到只在共享数据的实际做用域中才进行同步,使得须要同步的操做数量尽量变小,保证就算存在锁竞争,等待锁的线程也能尽快拿到锁。
但若是反复操做对同一个对象进行加锁和解锁,即便没有线程竞争,频繁地进行互斥同步操做也会致使没必要要的性能损耗,此时,虚拟机将会把加锁同步的范围粗化到整个操做序列的外部,这样只需加一次锁。
d.轻量级锁(Lightweight Locking)
首先先理解HotSpot虚拟机的对象头的内存布局:分为两部分
- 第一部分用于存储对象自身的运行时数据,这部分被称为Mark Word,是实现轻量级锁和偏向锁的关键。如哈希码、GC分代年龄等。
- 另一部分用于存储指向方法区对象类型数据的指针,若是是数组对象还会有一个额外的部分用于存储数组长度。
01
),虚拟机会在当前线程的栈帧中创建一个名为Lock Record的空间,用于存储锁对象Mark Word的拷贝。以下图。以后虚拟机会尝试用CAS操做将对象的Mark Word更新为指向Lock Record的指针。若更新动做成功,那么当前线程就拥有了该对象的锁,且对象Mark Word的锁标志位变为00
,即处于轻量级锁定状态;反之,虚拟机会先检查对象的Mark Word是否指向当前线程的栈帧,若当前线程已有该对象的锁,可直接进入同步块继续执行,不然说明改对象已被其余线程抢占。以下图。
另外,若是有两条以上的线程争用同一个锁,那轻量级锁就再也不有效,要膨胀为重量级锁,锁标志位变为
10
,Mark Word中存储的就是指向重量级锁的指针,后面等待锁的线程也要进入阻塞状态。
e.偏向锁(Biased Locking)
01
,即偏向模式,同时使用CAS操做把获取到这个锁的线程ID记录在对象的Mark Word中。若操做成功,持有偏向锁的线程之后每次进入这个锁相关的同步块时均可再也不进行任何同步操做。01
或轻量级锁定00
的状态,后续的同步操做就如轻量级锁执行过程。以下图。