Java基础

并发编程(三)synchronized实现原理

2021-01-10  本文已影响0人  Timmy_zzh
Java线程切换的实质

如下代码:

    private Object lock = new Object();
    private int value = 0;

    public void setValue() {
        synchronized (lock) {
            value++;
        }
    }

1.synchronized实现原理

对象头和Monitor

对象头

instanceOopDesc的基类为oopDesc类,结构如下:

class oopDesc {
    friend class VMStructs;
    private:
        volatile markOop  _mark;
        union _metadata {
            wideKlassOop  _klass;
            narrowOop     _compressed_klass;
    } _metadata;

32位Java虚拟机的Mark Word的默认存储结构如下:

1.32位jvm的Mark Word默认存储结构.png 2.Mark Word其他可能结构.png
是否偏向锁 锁标志位 锁状态
0 01 无锁
1 01 偏向锁
0 00 轻量级锁
0 10 重量级锁
0 11 GC标记

Monitor

ObjectMonitor实现锁同步机制

ObjectMonitor结构:

ObjectMonitor(){
    _header     = NULL;
    _count      = 0;    //记录个数
    _waiters    = 0;    
    _recursions = 0;    //锁重入次数
    _object     = NULL;
    _owner      = NULL; //指向持有ObjectMonitor对象的线程
    _WaitSet    = NULL; //处于wait状态的线程,会被加入到_WaitSet
    _WaitSetLock= 0;    
    _Responsible = NULL;
    _succ       = NULL;
    _cxq        = NULL;
    FreeNext    = NULL;
    _EntryList  = NULL; //处于等待锁block状态的线程,会被加入到该列表
    _SpinFreq   = 0;
    _SpinClock  = 0;
    OwnerIsThread =0;
}

其中几个比较关键的属性:

    _owner:     //指向持有ObjectMonitor对象的线程
    _WaitSet:   //存放处于wait状态的线程队列
    _EntryList://存放处于等待锁block状态的线程队列
    _recursions://锁的重入次数
    _count;     //记录该线程获取锁的次数

比如有3个线程分别执行以下同步代码块:

    private Object lock = new Object();

    public void synchMethod() {
        synchronized (lock) {
            // do something
        }
    }

1.锁对象是lock对象,jvm中每个对象实例都有一个对应的oopDesc实例,oopDesc实例中的标记字段(mark word)中的_mark 属性总有一个对应的ObjectMonitor对象,其内部结构如下:

3.1.ObjectMonitor内部对象结构.png

2.现在使用3个线程来执行上面的同步代码块。默认情况下,3个线程都会处于阻塞状态,会存放在ObjectMonitor中的EntrySet队列中,如下所示:

3.2.默认下线程处于阻塞状态,存放在EntrySet队列中.png

3.假设线程2首先通过竞争获取到了锁对象,则ObjectMonitor中的Owner指针指向线程2,并将count自加1,结果如下:

3.3.线程2竞争到锁对象,ObjectMonitor中的Owner指针指向线程2.png

4.上图中Owner指向线程2,表示线程2已经成功获取到锁(Monitor)对象,其他线程只能处于阻塞(blocking)状态。

3.4.wait操作实现.png

5.因为线程2释放了锁,线程1和线程3可以再次通过竞争去获取锁(Monitor)对象,如果线程1通过竞争获取到锁,则重新将Owner指向线程1,如下:

3.5.阻塞状态下线程竞争后重新获取锁.png

6.如果在线程1执行过程中调用了notify操作将线程2唤醒。

3.6.notify操作实现.png

7.当线程1中的代码执行完毕以后,同样会自动释放锁,以便其他线程再次获取锁对象

重量级锁
上一篇下一篇

猜你喜欢

热点阅读