抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

文章已收录Github精选,欢迎Starhttps://github.com/yehongzhi

前言

作为Java程序员,我们都知道在多线程的情况下,为了保证线程安全,经常会使用synchronized和Lock锁。Lock锁之前写过一篇《不得不学的AQS》,已经详细讲解过Lock锁的底层原理。这次我们讲一下日常开发中常用的关键字synchronized,想要用得好,底层原理必须要搞明白。

synchronized是JDK自带的一个关键字,在JDK1.5之前是一个重量级锁,所以从性能上考虑大部分人会选择Lock锁,不过毕竟是JDK自带的关键字,所以在JDK1.6后对它进行优化,引入了偏向锁,轻量级锁,自旋锁等概念。

一、synchronized的使用方式

在语法上,要使用synchronized关键字,需要把任意一个非null对象作为”锁”对象,也就是需要一个对象监视器(Object Monitor)。总的来说有三种用法:

1.1 作用在实例方法

修饰实例方法,相当于对当前实例对象this加锁,this作为对象监视器。

1
2
3
public synchronized void hello(){
System.out.println("hello world");
}

1.2 作用在静态方法

修饰静态方法,相当于对当前类的Class对象加锁,当前类的Class对象作为对象监视器。

1
2
3
public synchronized static void helloStatic(){
System.out.println("hello world static");
}

1.3 修饰代码块

指定加锁对象,对给定对象加锁,括号括起来的对象就是对象监视器。

1
2
3
4
5
6
public void test(){
SynchronizedTest test = new SynchronizedTest();
synchronized (test){
System.out.println("hello world");
}
}

二、synchronized锁的原理

在讲原理前,我们先讲一下Java对象的构成。在JVM中,对象在内存中分为三块区域:对象头,实例数据和对齐填充。如图所示:

对象头

  • Mark Word,用于存储对象自身运行时的数据,如哈希码(Hash Code),GC分代年龄,锁状态标志,偏向线程ID、偏向时间戳等信息,它会根据对象的状态复用自己的存储空间。它是实现轻量级锁和偏向锁的关键
  • 类型指针,对象会指向它的类的元数据的指针,虚拟机通过这个指针确定这个对象是哪个类的实例。
  • Array length,如果对象是一个数组,还必须记录数组长度的数据。

实例数据

  • 存放类的属性数据信息,包括父类的属性信息。

对齐填充

  • 由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

2.1 同步代码块原理

为了看底层实现原理,使用javap -v xxx.class命令进行反编