并发编程(十三)Synchronized锁升级-偏向锁(上)

88 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

image.png

1.偏向锁使用的前提:

至少JDK1.6 版本且开启了偏向锁配置

偏向锁在Java 6和Java 7里是默认启用的,但是它在应用程序启动几秒钟之后才激活,如有必要可以使用JVM参数来关闭延迟:-XX:BiasedLockingStartupDelay=0。如果你确定应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,那么程序默认会进入轻量级锁状态。

被加锁的对象,没有真正、或者隐式的调用父类 Object 里边的hashcode方法

这句话意思是没有主动调用例如使用一个对象的hashCode()方法,或者调用map.put(K,V)方法把对象放进去,因为map.put(K,V)方法源码里会调用hashCode()方法。

2.对于以上两点,我们举例子来说明:

示例代码如下:

public class SyncLockFlag {
    static MyObject myobject = new MyObject();
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=====================未偏向线程的偏向锁============================");
        System.out.println(ClassLayout.parseInstance(myobject).toPrintable());
        //myobject.hashCode();
        //HashMap map = new HashMap();
        //map.put(myobject,""); //隐式的调用了hashcode方法
        synchronized (myobject) {
            System.out.println("=====================偏向锁============================");
            System.out.println(ClassLayout.parseInstance(myobject).toPrintable());
        }
    }
    static class MyObject{
    }
}

执行效果如下所示:

image.png

我们可以根据红色箭头看到刚开始是无锁状态,下面为什么加完锁之后成了轻量级锁了呢? 因为我们没有使用 -XX:BiasedLockingStartupDelay=0这个参数,这个参数是立即开启偏向锁,我们进行如下的配置,在VM options中添加参数即可:

image.png 我们可以看到效果如下:

image.png 这里我们需要注意:

当我们开启了偏向锁,并且没有延迟开启的时候,新创建的对象的mark word 默认就是偏向锁状态的markword。

只不过这个时候,因为没有现成争抢,除了我们的锁标志为和是否为偏向锁标志位,其他的位数都是0。

当我们真正加锁后,markword中会记录线程id等信息。

如果此时放开代码中的hashMap注释,此时相当于隐形调用hashCode方法,结果如下图:

image.png 我们可以看到,刚开始依旧是偏向锁,后面变成了轻量级锁,为什么呢?

如果一旦调用了object的hashcode方法,那么我们的对象头里边就有真正的hashcode值了,如果偏向锁来进行markword的替换,至少要提供一个保存hashcode的地方吧?可惜的是,偏向锁并没有地方进行markword的保存,只有轻量级锁才会有“displace mark word”