Java并发 synchronized 的一些学习记录
jdk1.6 以后对 synchronized 进行了一些优化,包括偏向锁,轻量级锁,重量级锁等
这些锁的加锁方式大多跟对象头有关,我们可以查看 jdk 代码
首先对象头的位置注释1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1 2 3 4 5 6 7 enum { locked_value = 0 , unlocked_value = 1 , monitor_value = 2 , marked_value = 3 , biased_lock_pattern = 5 };
我们可以用 java jol库来查看对象头,通过一段简单的代码来看下1 2 3 4 5 6 public class ObjectHeaderDemo { public static void main (String[] args) throws InterruptedException { L l = new L (); System.out.println(ClassLayout.parseInstance(l).toPrintable()); } }
然后可以看到打印输出,当然这里因为对齐方式,我们看到的其实顺序是反过来的,按最后三位去看,我们这是 001,好像偏向锁都没开,这里使用的是 jdk1.8,默认开始偏向锁的,其实这里有涉及到了一个配置,jdk1.8 中偏向锁会延迟 4 秒开启,可以通过添加启动参数 -XX:+PrintFlagsFinal,看到
因为在初始化的时候防止线程竞争有大量的偏向锁撤销升级,所以会延迟 4s 开启
我们再来延迟 5s 看看1 2 3 4 5 6 7 public class ObjectHeaderDemo { public static void main (String[] args) throws InterruptedException { TimeUnit.SECONDS.sleep(5 ); L l = new L (); System.out.println(ClassLayout.parseInstance(l).toPrintable()); } }
可以看到偏向锁设置已经开启了,我们来是一下加个偏向锁1 2 3 4 5 6 7 8 9 10 11 12 13 public class ObjectHeaderDemo { public static void main (String[] args) throws InterruptedException { TimeUnit.SECONDS.sleep(5 ); L l = new L (); System.out.println(ClassLayout.parseInstance(l).toPrintable()); synchronized (l) { System.out.println("1\n" + ClassLayout.parseInstance(l).toPrintable()); } synchronized (l) { System.out.println("2\n" + ClassLayout.parseInstance(l).toPrintable()); } } }
看下运行结果
可以看到是加上了 101 = 5 也就是偏向锁,后面是线程 id
当我再使用一个线程来竞争这个锁的时候1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ObjectHeaderDemo { public static void main (String[] args) throws InterruptedException { TimeUnit.SECONDS.sleep(5 ); L l = new L (); System.out.println(ClassLayout.parseInstance(l).toPrintable()); synchronized (l) { System.out.println("1\n" + ClassLayout.parseInstance(l).toPrintable()); } Thread thread1 = new Thread () { @Override public void run () { try { TimeUnit.SECONDS.sleep(5L ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (l) { System.out.println("thread1 获取锁成功" ); System.out.println(ClassLayout.parseInstance(l).toPrintable()); try { TimeUnit.SECONDS.sleep(5L ); } catch (InterruptedException e) { e.printStackTrace(); } } } }; thread1.start(); } }
可以看到变成了轻量级锁,在线程没有争抢,只是进行了切换,就会使用轻量级锁,当两个线程在竞争了,就又会升级成重量级锁1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class ObjectHeaderDemo { public static void main (String[] args) throws InterruptedException { TimeUnit.SECONDS.sleep(5 ); L l = new L (); System.out.println(ClassLayout.parseInstance(l).toPrintable()); synchronized (l) { System.out.println("1\n" + ClassLayout.parseInstance(l).toPrintable()); } Thread thread1 = new Thread () { @Override public void run () { try { TimeUnit.SECONDS.sleep(5L ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (l) { System.out.println("thread1 获取锁成功" ); System.out.println(ClassLayout.parseInstance(l).toPrintable()); try { TimeUnit.SECONDS.sleep(5L ); } catch (InterruptedException e) { e.printStackTrace(); } } } }; Thread thread2 = new Thread () { @Override public void run () { try { TimeUnit.SECONDS.sleep(5L ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (l) { System.out.println("thread2 获取锁成功" ); System.out.println(ClassLayout.parseInstance(l).toPrintable()); } } }; thread1.start(); thread2.start(); } } class L { private boolean myboolean = true ; }
可以看到变成了重量级锁。