锁的优化策略安全
编码过程当中可采起的锁优化的思路有如下几种:多线程
1:减小锁持有时间 并发
例如:对一个方法加锁,不如对方法中须要同步的几行代码加锁;高并发
2:减少锁粒度性能
例如:ConcurrentHashMap采起对segment加锁而不是整个map加锁,提升并发性;优化
3:锁分离 编码
根据同步操做的性质,把锁划分为的读锁和写锁,读锁之间不互斥,提升了并发性。操作系统
4:锁粗化 线程
这看起来与思路1有冲突,其实否则。思路1是针对一个线程中只有个别地方须要同步,因此把锁加在同步的语句上而不是更大的范围,减小线程持有锁的时间;对象
而锁粗化是指:在一个间隔性地须要执行同步语句的线程中,若是在不连续的同步块间频繁加锁解锁是很耗性能的,所以把加锁范围扩大,把这些不连续的同步语句进行一次性加锁解锁。虽然线程持有锁的时间增长了,可是整体来讲是优化了的。
5:锁消除
锁消除是编译器作的事:根据代码逃逸技术,若是判断到一段代码中,堆上的数据不会逃逸出当前线程(即不会影响线程空间外的数据),那么能够认为这段代码是线程安全的,没必要要加锁。
Java虚拟机中采起的锁优化策略:
1:偏向锁:锁对象偏向于当前得到它的线程,若是在接下来的没有被其余线程请求,则持有该锁的线程将再也不须要进行同步操做(即:持有该锁的线程在接下来的执行中遇到同步块时再也不须要lock和unlock了,直接执行便可)。当另外一个线程申请该锁时,当前线程的偏向模式才会结束,让出该锁。
2:轻量级锁:syncrhoized的底层实现是经过监视器monitor来控制的,而monitorenter与monitorexit这两个原语是依赖操做系统互斥(mutex)来实现的。
互斥会致使线程挂起,并在较短的时间内又须要从新调度回原线程的,较为消耗资源。轻量级锁(Lightweight Locking)利用了CPU原语Compare-And-Swap(CAS,汇编指令CMPXCHG),尝试在进入互斥前,进行补救,减小多线程进入互斥的概率。
若是偏向锁失败,那么系统会进行轻量级锁的操做,使用CAS操做来尝试加锁。若是轻量级锁失败,才调用系统级别的重量级锁(syncrhoized)来加锁。
3:自旋锁:当线程申请锁时,锁被占用,则让当前线程执行一个忙循环(自旋),看看持有锁的线程是否会很快释放锁。若是自旋后还没得到锁,才进入同步阻塞状态;
3.1:自适应自旋:自旋的线程自旋的时间为同一个锁上一次线程自旋并得到锁的耗时。若是对于这个锁,自旋不多有成功的,就不自旋了,避免浪费CPU资源。
为了尽可能避免使用重量级锁(操做系统层面的互斥),JVM首先会尝试轻量级锁,轻量级锁会尝试使用CAS操做来得到锁,若是轻量级锁得到失败,说明存在竞争。可是也许很快就能得到锁,就会尝试自旋锁,将线程作几个空循环,每次循环时都不断尝试得到锁。若是自旋锁也失败,那么只能升级成重量级锁。