synchronized是非公平的锁!有线程在执行,新进入的线程会进入这个cxq这个队列中!本文释放锁分析使用的是默认策略(QMode=0):如果EntryList为空,则将cxq中的元素按原有顺序插入到EntryList,并唤醒第一个线程,也就是当EntryList为空时,是后来的线程先获取锁。_EntryList不为空,直接从_EntryList中唤醒线程。 synchronized到底是什么?
synchronized是JVM内置锁,基于Monitor机制实现。依赖底层 *** 作系统的互斥原语Mutex(互斥量)。表面上它是一个重量级锁,性能较低。实际上JVM内置锁在1.5之后版本做了重大的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、自适应自旋(Adaptive Spinning)等技术来减少锁 *** 作的开销,内置锁的并发性能已经基本与Lock持平。 Monitor(管程/监视器)
Monitor,直译为“监视器”,而 *** 作系统领域一般翻译为“管程”。管程是指管理共享变量以及对共享变量 *** 作的过程,让它们支持并发。synchronized关键字和wait()、notify()、notifyAll()这三个方法是Java中实现管程技术的组成部分。 管程模型
在管程的发展史上,先后出现过三种不同的管程模型,分别是Hasen模型、Hoare模型和MESA模型。现在正在广泛使用的是MESA模型。
wait()、notify()和notifyAll()的使用使用wait有个范式要求:while(条件不满足) { wait(); }所有等待线程拥有相同的等待条件:使用notify()。所有等待线程被唤醒后,执行相同的 *** 作:使用notify()。只需要唤醒一个线程:使用notify()。其他时候尽量使用notifyAll()。 Java内置的管程:synchronized
Java 参考了 MESA 模型,语言内置的管程(synchronized)对 MESA 模型进行了精简。MESA 模型中,条件变量可以有多个,Java 语言内置的管程里只有一个条件变量。 Monitor机制在Java中的实现
java.lang.Object 类定义了 wait(),notify(),notifyAll() 方法wait(),notify(),notifyAll()的具体实现,依赖于 ObjectMonitor(JVM内部的机制) 实现。 ObjectMonitor的主要数据结构
_header = NULL; //对象头 markOop _count = 0; _waiters = 0, _recursions = 0; // synchronized是一个重入锁,这个变量记录锁的重入次数 _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;synchronized的等待唤醒机制
在获取锁时,是将当前线程插入到cxq的头部。在释放锁时默认策略(QMode=0):如果EntryList为空,则将cxq中的元素按原有顺序插入到EntryList,并唤醒第一个线程,也就是当EntryList为空时,是后来的线程先获取锁。_EntryList不为空,直接从_EntryList中唤醒线程。 synchronized下线程的执行流程:等待机制!
看一段代码的执行结果
public class SyncQModeDemo { public static void main(String[] args) throws InterruptedException { SyncQModeDemo demo = new SyncQModeDemo(); demo.startThreadA(); // 控制线程执行时间 Thread.sleep(100); demo.startThreadB(); Thread.sleep(100); demo.startThreadC(); } final Object lock = new Object(); public void startThreadA() { new Thread(() -> { synchronized (lock) { log.debug("A get lock"); try { lock.wait(300); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("A release lock"); } }, "thread-A").start(); } public void startThreadB() { new Thread(() -> { synchronized (lock) { try { log.debug("B get lock"); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("B release lock"); } }, "thread-B").start(); } public void startThreadC() { new Thread(() -> { synchronized (lock) { log.debug("C get lock"); } }, "thread-C").start(); } }执行结果
A get lockB get lockB release lockA release lockC get lock 为什么是这样的结果?
第一个线程正常执行:owner是第一个线程!第二个线程进来,由于第一个在执行,他会阻塞:owner是第一个线程,cxq有第二个线程!假设这时候线程一调用wait()方法:WaitSet有第一个线程,cxq有第二个线程!owner为空!下一次进行争抢线程的使用权,EntryList是空的,cxq中的第线程去执行:WaitSet有第一个线程,owner是第二个线程!这时候第三个线程进来:cxq有第三个线程,WaitSet有第一个线程,cxq有第三个线程!owner是第二个线程!第二个线程执行完毕,唤醒其他线程,将WaitSet中的线程转移到EntryList:EntryList有第一个线程,cxq有第三个线程!下一次进行争抢线程的使用权,EntryList有值,直接从EntryList里面唤醒线程:EntryList有第一个线程,owner是第一个线程!第一个线程执行完毕,唤醒线程,只有cxq里面有线程,唤醒他:owner是第三个线程。 synchronized下线程的执行流程:竞争机制
看一段代码的执行结果
public class SyncQModeDemo { public static void main(String[] args) throws InterruptedException { SyncQModeDemo demo = new SyncQModeDemo(); demo.startThreadA(); // 控制线程执行时间 Thread.sleep(100); demo.startThreadB(); Thread.sleep(100); demo.startThreadC(); } final Object lock = new Object(); public void startThreadA() { new Thread(() -> { synchronized (lock) { log.debug("A get lock"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("A release lock"); } }, "thread-A").start(); } public void startThreadB() { new Thread(() -> { synchronized (lock) { try { log.debug("B get lock"); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("B release lock"); } }, "thread-B").start(); } public void startThreadC() { new Thread(() -> { synchronized (lock) { log.debug("C get lock"); } }, "thread-C").start(); } }执行结果
A get lockA release lockC get lockB get lockB release lock 为什么是这样的结果?
第一个线程正常执行:owner是第一个线程!第一个线程没执行完,第二个线程进来:owner是第一个线程!cxq中有第二个线程!第一个线程没执行完,第三个线程进来:owner是第一个线程!cxq中有第二个线程,第三个线程(队列结构,第二个线程先进来,第三个线程后进来)!第一个线程执行完,原顺序从cxq中转移线程到EntryList:EntryList中有第二个线程,第三个线程(队列结构,第二个线程先进来,第三个线程后进来)!唤醒线程:owner是第三个线程!cxq中有第二个线程!线程三执行完:唤醒第二个线程,执行! 结束语
获取更多有价值的文章,让我们一起成为架构师!关注公众号,可以让你对MySQL有非常深入的了解关注公众号,每天持续高效的了解并发编程!这个公众号,无广告!!!每日更新!!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)