网站基本框架,免费购物商城网站建设,wordpress禁止中国ip,自建网站支付问题笔试#xff1a;画出Synchronized 线程状态流转实现原理图 synchronized关键字解决的是多个线程之间访问资源的同步性#xff0c;synchronized 翻译为中文的意思是同步#xff0c;也称之为”同步锁“。
synchronized的作用是保证在同一时刻#xff0c; 被修饰的代码块或方…笔试画出Synchronized 线程状态流转实现原理图 synchronized关键字解决的是多个线程之间访问资源的同步性synchronized 翻译为中文的意思是同步也称之为”同步锁“。
synchronized的作用是保证在同一时刻 被修饰的代码块或方法只会有一个线程执行以达到保证并发安全的效果。 synchronized关键字可以实现什么类型的锁 悲观锁synchronized关键字实现的是悲观锁每次访问共享资源时都会上锁。 非公平锁synchronized关键字实现的是非公平锁即线程获取锁的顺序并不一定是按照线程阻塞的顺序。 可重入锁synchronized关键字实现的是可重入锁即已经获取锁的线程可以再次获取锁。 独占锁或者排他锁synchronized关键字实现的是独占锁即该锁只能被一个线程所持有其他线程均被阻塞。 Synchronized的使用方式 主要有3种使用方式:
1.修饰实例方法作用于当前实例加锁 public synchronized void method(){ // 代码 }
2.修饰静态方法作用于当前类对象加锁 public static synchronized void method(){ // 代码 }
3.修饰代码块指定加锁对象对给定对象加锁 synchronized(this){ //代码 } Synchronized的底层实现 synchronized的底层实现是完全依赖JVM虚拟机的,所以谈synchronized的底层实现就不得不谈数据在JVM内存的存储Java对象头以及Monitor对象监视器。
1.Java对象头 在JVM虚拟机中对象在内存中的存储布局可以分为三个区域: 对象头(Header) 实例数据(Instance Data) 对齐填充(Padding) Java对象头主要包括两部分数据 1)类型指针Klass Pointer
是对象指向它的类元数据的指针虚拟机通过这个指针来确定这个对象是哪个类的实例;
2)标记字段(Mark Word)
用于存储对象自身的运行时数据如哈希码HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等,它是实现轻量级锁和偏向锁的关键.
所以很明显synchronized使用的锁对象是存储在Java对象头里的标记字段里。
2.Monitor
monitor描述为对象监视器,可以类比为一个特殊的房间这个房间中有一些被保护的数据monitor保证每次只能有一个线程能进入这个房间进行访问被保护的数据进入房间即为持有monitor退出房间即为释放monitor。
使用syncrhoized加锁的同步代码块在字节码引擎中执行时主要就是通过锁对象的monitor的取用(monitorenter)与释放(monitorexit)来实现的。
首先来看在方法上上锁我们就新定义一个同步方法然后进行反编译查看其字节码 可以看到在add方法的flags里面多了一个ACC_SYNCHRONIZED标志这标志用来告诉JVM这是一个同步方法在进入该方法之前先获取相应的锁锁的计数器加1方法结束后计数器-1如果获取失败就阻塞住知道该锁被释放。
从反编译的同步代码块可以看到同步块是由monitorenter指令进入然后monitorexit释放锁在执行monitorenter之前需要尝试获取锁如果这个对象没有被锁定或者当前线程已经拥有了这个对象的锁那么就把锁的计数器加1。当执行monitorexit指令时锁的计数器也会减1。当获取锁失败时会被阻塞一直等待锁被释放。
但是为什么会有两个monitorexit呢其实第二个monitorexit是来处理异常的仔细看反编译的字节码正常情况下第一个monitorexit之后会执行goto指令而该指令转向的就是23行的return也就是说正常情况下只会执行第一个monitorexit释放锁然后返回。而如果在执行中发生了异常第二个monitorexit就起作用了它是由编译器自动生成的在发生异常时处理异常然后释放掉锁。 3.线程状态流转在Monitor上体现
当多个线程同时请求某个对象监视器时对象监视器会设置几种状态用来区分请求的线程
Contention List所有请求锁的线程将被首先放置到该竞争队列 Entry ListContention List中那些有资格成为候选人的线程被移到Entry List Wait Set那些调用wait方法被阻塞的线程被放置到Wait Set OnDeck任何时刻最多只能有一个线程正在竞争锁该线程称为OnDeck Owner获得锁的线程称为Owner !Owner释放锁的线程 每一个锁都对应一个monitor对象在HotSpot虚拟机中它是由ObjectMonitor实现的C实现。每个对象都存在着一个monitor与之关联对象与其monitor之间的关系有存在多种实现方式如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成但当一个monitor被某个线程持有后它便处于锁定状态。
ObjectMonitor() { _header NULL; _count 0; //锁的计数器获取锁时count数值加1释放锁时count值减1 _waiters 0, //等待线程数 _recursions 0; // 线程重入次数 _object NULL; // 存储Monitor对象 _owner NULL; // 持有当前线程的owner _WaitSet NULL; // wait状态的线程列表 _WaitSetLock 0 ; _Responsible NULL ; _succ NULL ; _cxq NULL ; // 阻塞在EntryList上的单向线程列表 FreeNext NULL ; _EntryList NULL ; // 处于等待锁状态block状态的线程列表 _SpinFreq 0 ; _SpinClock 0 ; OwnerIsThread 0 ; _previous_owner_tid 0; }
其中 _owner、_WaitSet和_EntryList 字段比较重要它们之间的转换关系如下图 ObjectMonitor中有两个队列_WaitSet和_EntryList用来保存ObjectWaiter对象列表(每个等待锁的线程都会被封装ObjectWaiter对象)_owner指向持有ObjectMonitor对象的线程当多个线程同时访问一段同步代码时首先会进入_EntryList 集合当线程获取到对象的monitor 后进入 _Owner 区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1若线程调用 wait() 方法将释放当前持有的monitorowner变量恢复为nullcount自减1同时该线程进入 WaitSe t集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值以便其他线程进入获取monitor(锁)。 monitor对象存在于每个Java对象的对象头中(存储的指针的指向)synchronized锁便是通过这种方式获取锁的也是为什么Java中任意对象可以作为锁的原因同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因(关于这点稍后还会进行分析) 知识来源
Synchronized的底层实现原理(看这篇就够了)_synchronized底层实现原理_mikechen的互联网架构的博客-CSDN博客
https://www.cnblogs.com/wffzk/p/16639472.html
深入理解synchronized底层原理一篇文章就够了 - 知乎