一、锁消除
这个优化是由JIT来实现的,如果你的方法内,没有涉及到那些临界资源的使用,那么JIT会将synchronized去掉。
我们用代码来举个例子。
public synchronized void method(){
Person p = new Person();
p.age = 18;
}
上面这段代码,被JIT优化后,会把synchronized去掉,注意,是经过JIT编译后才会去掉!!
原始代码依旧是保留synchronized的。
public void method(){
Person p = new Person(); // 每个线程进来都new一个新的对象,整个方法的操作是安全的,加不加锁都一样
p.age = 18;
}
二、锁范围调整
这个也是JIT来做的优化,他会在不影响代码正常业务的情况下,把锁住的代码范围提高。
我们依旧拿代码举例子
此方法中,synchronized的粒度过小了,并且没有什么需要在循环中争抢的资源,可以考虑优化
public void method(){
for(int i = 0; i < 999; i++){
synchronized(this){
// 业务代码
}
}
}
那么优化后的结果如下,相当于我一进来就把锁拿了,然后再执行循环,这样就只有1次加锁和解锁操作。
public void method(){
synchronized(this){
for(int i = 0; i < 999; i++){
// 业务代码...
}
}
}
三、锁升级
原来的synchronized是纯重量级锁,就是一上来,先抢锁,没抢到的全部挂起等待(用户态和内核态切换,开销大)
于是,JDK1.6就进行了优化。
首先一上来,他是一个无锁的状态,当有线程获得锁了,那么升级为偏向锁,偏向锁也还没有真正的上锁,他在对象头上,记录持有线程的ID,然后下次过来,先比较ID,如果是同一个线程ID,则直接放行,这个过程没有上锁解锁操作。
如果现在另外一个线程进入了,那么就升级为CAS机制的锁资源争抢,就是线程A获取到资源了,那么线程B使用CAS自旋的方式不断的尝试获取锁。如果长时间获取不到,或者后面又来了C、D、E、F...等多个线程同时争抢,那么最终就会升级到重量级锁。
重量级锁就会涉及到用户态和内核态的一个切换,但是也比大量的CAS要好一些。
所以注意了,并不是任何时候CAS争抢都要比重量级锁好的,CAS自旋的时候,消耗了CPU资源,但是大部分时间其实是空转的一个情况,因为你抢不到锁,10个线程同时争抢,只有1个线程抢成功,其他9个就必须自旋。