synchronized底层原理

synchronized是JVM内置锁,基于monitor机制实现,依赖底层操作系统的互斥源语Mutex(互斥量),它是一个重量级锁,性能较低。
当然,JVM内置锁在1.5之后版本做了重大优化,如锁粗化,锁消除、轻量级锁、偏向锁、自适应锁、等技术来减少操作的开销,内置锁的并发性能已经基本与lock持平。

java虚拟机通过一个同步结构支持方法和方法中的指令序列的同步:monitor。

同步方法是通过方法access_flags中设置ACC_SYNCHRONIZED标志来实现;
同步代码块是通过 ** monitorenter ** 和 ** montorexit ** 来实现。
两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现的,被阻塞的线程会被挂起、等待重新调度,
会导致 **用户态和内核态** 两个态之间来回切换,对性能有较大影响。

Monitor (管程/监视器):

Monitor, 直译为监视器,而操作系统领域一般翻译为 管程。 管程是指管理共享变量以及对共享变量操作的过程,让它们支持并发。
在java1.5之前,java语言提供的唯一并发语言就是管程,java1.5之后提供的SDK并发包也是以管程为基础的。除了java之外,C/C++
C#等高级语言也都是支持管程的。synchronized关键字和wait() notify() notifyAll() 这三个方法是java中实现管程技术的组成部分。

MESA模型:

在管程的发展史上,先后出现过三种不同的管程模型,分别是Hasen管程,Hoare模型和MESA模型,现在正在广泛使用的是MESA模型。
在这里插入图片描述
管程中引入了条件变量的概念,而且每个条件变量都对应有一个等待队列。条件变量和等待队列的作用是解决线程之间的同步问题

wait()的正确使用姿势:
对于MESA管程来说,有一个编程范式:

while(条件不满足){
  wait();
}

唤醒的时间和获取到锁继续执行的时间是不一致的,被唤醒的线程再次执行时可能条件又不满足了,所以循环检验条件。
MESA模型的wait() 方法还有一个超时参数,为了避免线程进入等待队列和永久阻塞。

notify()和notifyAll()分别何时使用:
满足一下三个条件,可以使用notify(), 其余情况尽可能使用notifyAll():
1.所有等待线程拥有相同的等待条件
2.所有等待线程被唤醒后,执行相同的操作
3.只需要唤醒一个线程

java语言的内置管程synchronized

Java参考了MESA模型,语言内置的管程(synchronized)对MESA模型进行了精简。MESA模型中,条件变量可以有多个,Java语言内置的管程里面只用一个条件变量。

在这里插入图片描述

Monitor机制在Java中的实现:

java.lang.Object 类定义了wait(), notify(), notifyAll() 方法,这些方法的具体实现,依赖于ObjectMonitor实现,这是JVM 内部基于C++实现的一套机制。
ObjectMonitor 其主要数据结构如何:

ObjectMonitor() {
    _header       = NULL; //对象头  markOop
    _count        = 0;  
    _waiters      = 0,   
    _recursions   = 0;   // 锁的重入次数 
    _object       = NULL;  //存储锁对象
    _owner        = NULL;  // 标识拥有该monitor的线程(当前获取锁的线程) 
    _WaitSet      = NULL;  // 等待线程(调用wait)组成的双向循环链表,_WaitSet是第一个节点
    _WaitSetLock  = 0 ;    
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ; //多线程竞争锁会先存到这个单向链表中 (FILO栈结构)
    FreeNext      = NULL ;
    _EntryList    = NULL ; //存放在进入或重新进入时被阻塞(blocked)的线程 (也是存竞争锁失败的线程)
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;

在这里插入图片描述

在获取锁的时候,是将当前线程插入到cxq的头部,而释放锁时候,默认策略Qmode=0是: 如果EntryList为空,则将cxq中的元素按原有顺序插入到EtryList,并唤醒第一个线程,也就是当EntryList为空时,是后来的线程先获取锁。EntryList不为空,直接从EntryList中唤醒线程。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值