一、synchronized的使用场景
synchronized一般使用在下面这几种场景:
- 修饰代码块,指定一个加锁的对象,给对象加锁
public Demo1{
Object lock=new Object();
public void test1(){
synchronized(lock){
}
}
}
- 修饰静态方法,对当前类的Class对象加锁
public class Demo2 {
//形式一
public void test1(){
synchronized(Synchronized.class){
}
}
//形式二
public void test2(){
public synchronized static void test1(){
}
}
}
- 修饰普通方法,对当前实例对象this加锁
public class Demo3 {
public synchronized void test1(){
}
}
二、JVM中,对象在内存中的布局
synchronized实现的锁是存储在Java对象头。所以要对synchronized深入理解,首先了解一下对象在内存中的布局怎样的?
在 JVM 中,对象在内存中分为这么三块区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。
1、对象头(Header)两个部分组成:markOop或称为Mark Word和类元信息
Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。源码如下:
class markOopDesc: public oopDesc {
private:
// Conversion
uintptr_t value() const {
return (uintptr_t) this; }
public:
// Constants
enum {
age_bits = 4, //分代年龄
lock_bits = 2, //锁标识
biased_lock_bits = 1, //是否为偏向锁
max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits, //对象的hashcode
cms_bits = LP64_ONLY(1) NOT_LP64(0),
epoch_bits = 2 //偏向锁的时间戳
};
类元信息:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
2、实例数据:这部分主要是存放类的数据信息,父类的信息
3、对齐填充
三、synchronized底层从字节码聊起
首先,来看使用synchronized修饰的方法及方法块的小Demo案例,然后进行分析。由于synchronized的实现是在JVM层面的,我们要深入理解,那就是从字节码聊起了。
public class Demo03 {
public static void main( String[] args ){
System.out.println( "hello Java" );
}
//synchronized修饰普通方法
public synchronized void test1() {
}