synchronize

## Java对象头(64位虚拟机)

- 整个对象头一共有128位,Mark Word有64位,Klass Word有64位,但是Klass Word因为指针压缩的原因被压缩为32位,使用对象头一共有96位,而且现在我们更需要关注的是前64位。至于Klass Word指向了方法区的模板类,字节码。

- 对象头还不是一成不变的,就表格可以看出,对象的状态会改变对象头的数值,这里我们分为5个状态,分别是无锁(001)、偏向锁(101)、轻量锁(00)、重量锁(10)和被gc标记(11)的对象。

- ![64位虚拟机对象头.png](https://upload-images.jianshu.io/upload_images/19480260-d33ae97a21f2158e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### 对象头占据内存

- 例如:int是4个字节,Integer对象的Mark Word8字节,Klass Word8个字节(指针压缩后4字节),存储占4字节,存储一个数需要占8(Mark Word)+4(Klass Word)+4(存储占4字节)=16字节

**验证new Integer(1)占据内存大小**

```

       

            org.openjdk.jol

            jol-core

            0.9

       


@Slf4j(topic = "ants.TestIntegerSize")

public class TestIntegerSize {

    public static void main(String[] args) {

        Integer i = new Integer(1);

        log.debug(ClassLayout.parseInstance(i).toPrintable());

    }

}


# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

2020-04-12 17:45:45.202 [main] DEBUG ants.TestIntegerSize 16 - java.lang.Integer object internals:

OFFSET  SIZE  TYPE DESCRIPTION                              VALUE

      0    4        (object header)                          01 00 00 00 (00000001 00000000 00000000 00000000) (1)

      4    4        (object header)                          00 00 00 00 (00000000 00000000 00000000 00000000) (0)

      8    4        (object header)                          ae 22 00 f8 (10101110 00100010 00000000 11111000) (-134208850)

    12    4    int Integer.value                            1

Instance size: 16 bytes

Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

```

- 由上面控制台结果分析,new Integer(1)会占据16字节,而我们知道int i=1只会占四个字节。

## Monitor(锁)  监视器|管程

- 每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级锁)之后,该对象头的Mark Word就被设置指向Monitor对象指针。

![线程-对象-Monitor关系.png](https://upload-images.jianshu.io/upload_images/19480260-af51190166c05ef3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

0. synchronized(obj)使用对象锁(obj)和Monitor对象一一对应

1. 刚开始Monitor中Owner为null

2. 当Thread-2执行synchronized就会将Monitor的所有者Owner置为Thread-2,Monitor中只能有一个Owner

3. Thread在上锁过程中,如果Thread-3,thread-4,thread-5也来执行synchronized(obj),就会进入EntryList中进阻塞(BLOCKED)状态

4. Thread-2执行完同步代码块内容,然后唤醒EntryList中等待的线程来竞争锁,竞争时是非公平的(synchronized没有提供公平锁,非公平是指如果Thread-2释放锁的时候来了Thread-6,那么Thread-3和Thread-6将同时有机会获得obj对象)

5. 图中Thread-0,Therad-1是之前获得过锁,但是条件不满足进入WAITING状态的线程(跟wait-notify相关)

### 注意

- synchronized必须是关联统一个对象时,才会出现上面结果

- 不加synchronized不会关联monitor监视器,不会出现上面结果

### synchronized原理

![代码.png](https://upload-images.jianshu.io/upload_images/19480260-45e4a4540dde0f81.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![代码中对应的字节码.png](https://upload-images.jianshu.io/upload_images/19480260-6c3091eb7efffab0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- dup:将lock复制一份

- Exception table:检测一定范围,如果范围内出现异常做处理

- 6-16-19-any,表示如果在6-6行出现异常,java会在19行开始执行,同样保存lock引用,将对象头重置,唤醒EntryList,让其他线程竞争锁,最后将处理不了的异常抛出去

- 在字节码中可以看出,即使synchronized中出现异常,synchronized也会释放锁

### 不同锁

- Monitor

- 轻量级锁

- 偏向锁

- 批量重偏向(一个类的偏向锁撤销达到20阈值)

- 一个类的偏向锁撤销达到40阈值,对象升级为轻量级锁

#### 轻量级锁

- 使用场景:如果一个对象虽然有多线程访问,单多线程访问的时间是错开的(没有线程竞争),name可以用轻量级锁来优化。

- 语法:轻量级锁对使用这透明,仍然使用synchronized

**轻量级锁加锁、解锁流程**

```

public class LightLock {

    static final Object object = new Object();


    public static void method1(){

        synchronized (object){

            // 同步代码块1

            method2();

        }

    }

    public static void method2(){

        synchronized (object){

            // 同步代码块2

        }

    }

}

```

1. 创建锁记录(Lock Record)对象,每个线程的栈帧都会包含一个锁记录,内部可以存储锁定对象的Mark Word

![image.png](https://upload-images.jianshu.io/upload_images/19480260-0cdeb93ec46bf7a5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

2. 让锁记录中Object Reference指向锁对象,并尝试使用cas替换Object的Mark Word的值存入锁记录

![image.png](https://upload-images.jianshu.io/upload_images/19480260-d2b8aa6481000ba1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

3. 如果替换成功,对象头中存储了锁记录地址和状态00(代表轻量级锁),表示该线程给对象加锁,--(object对象头中Mark Word发生变化,原先01无锁状态变成00轻量级锁状态,另外分代年龄等信息变为锁记录地址),--(锁记录里面将记录hash码、分代年龄等信息)(--锁记录和Mark Word信息互换)

![image.png](https://upload-images.jianshu.io/upload_images/19480260-2df7e6da608599aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

4. 如果cas失败,分为两种情况

4.1 如果是其他线程已经持有了该Object的轻量级锁,这时表名有竞争,进入锁膨胀过程

4.2 如果是自己执行了synchronized表示锁重入,那么再添加一条Lock Record作为重入的计数

![image.png](https://upload-images.jianshu.io/upload_images/19480260-9ecec6a6953defce.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

5. 当退出synchronized代码块(解锁时),如果取值为null的锁记录,这时重置锁记录,表示重入计数减一

![image.png](https://upload-images.jianshu.io/upload_images/19480260-8457230b5554ad13.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

6. 当退出synchronized代码块(解锁时),如果取值不为null的锁记录,这时cas将Mark Word的值恢复给对象头

- 成功,则解锁成功

- 失败,说明轻量

你可能感兴趣的:(synchronize)