锁优化的思路:
1.减少锁的持有时间(对需要同步的几行代码进行加锁)
2.减少锁的粒度 (ConcurrentHashMap采取对segment加锁而不是整个map加锁)
3.锁分离(将锁划分为读锁和写锁,相互不互斥)
4.锁粗化(一个间隔性地需要执行同步语句的线程中,如果在不连续的同步块间频繁加锁解锁是很耗性能的,因此把加锁范围扩大总体来说是优化的)
Java虚拟机采取的锁优化
1.偏向锁:锁对象偏向于当前获得它的线程,如果在接下来没有其他线程请求,则持有锁的线程将不再进行同步操作。(持有该锁的线程在接下来执行中遇到同步代码块不需要lock和unlock)。当另一个线程申请该锁,当前线程的偏向模式结束,让出该锁
2.轻量级锁:syncrhoized的底层实现是通过监视器monitor来控制的,而monitorenter与monitorexit这两个原语是依赖操作系统互斥(mutex)来实现的。互斥会导致线程挂起,并在较短时间内重新调度原程序,较消耗资源。轻量级锁利用CPUCAS指令,尝试进入互斥前进行补救,减少多线程进入互斥几率。如果偏向锁失败,那么系统进行轻量锁的操作,使用CAS操作尝试加锁。轻量锁失败才会调用系统级别的重量锁来加锁。
3.自旋锁: 当线程申请锁时,锁被占用,则让当前线程执行一个忙循环(自旋),看看持有锁的线程是否会很快释放锁。如果自旋后还没获得锁,才进入同步阻塞状态;
3.1自适应自旋:自旋的线程自旋的时间为同一个锁上一次线程自旋并获得锁的耗时。如果对于这个锁,自旋很少有成功的,就不自旋了,避免浪费CPU资源。为了尽量避免使用重量级锁(操作系统层面的互斥),JVM首先会尝试轻量级锁,轻量级锁会尝试使用CAS操作来获得锁,如果轻量级锁获得失败,说明存在竞争。但是也许很快就能获得锁,就会尝试自旋锁,将线程做几个空循环,每次循环时都不断尝试获得锁。如果自旋锁也失败,那么只能升级成重量级锁。