当前位置: 首页 > 后端技术 > Java

Java并发——sychronized关键字

时间:2023-04-01 20:39:19 Java

大家好,我是奇喵小屋,一个分享技术和生活的博主。以下是我的主页。各首页同步更新优质博客。创建起来并不容易。还请点开掘金主页了解主页Segmentfault主页开源中国更多MySQL、Redis、并发、JVM、分布式等面试热点知识稍后会在主页发布,还有Java学习路线、面试重点、职业规划、面经等相关博文转载请注明出处!1.synchronized实现的前提1.1对象监视器monitor任何一个对象都有一个与之关联的监视器对象。Monitor提供线程阻塞和唤醒机制。当监视器被线程持有时,它将处于锁定状态(获取锁)ObjectMonitor(){_header=NULL;_count=0;//锁计数器,sychronized是可重入锁_waiters=0,_recursions=0;_object=NULL;_owner=NULL;//识别持有锁的线程_WaitSet=NULL;//等待队列,与Object的wait()和notify()有关_WaitSetLock=0;_Responsible=NULL;_成功=空;_cxq=空;FreeNext=NULL;_EntryList=NULL;//同步队列,尝试获取锁线程会进入EntryList,如果成功获取锁,设置monitor的_owner为,如果没有成功获取锁,在EntryList中阻塞等待_SpinFreq=0;_自旋时钟=0;所有者是线程=0;}核心字段_count:计数器,如果为0,表示没有线程获取到锁。如果不为0,则表示有一个线程获取了锁(可以实现可重入锁,每次持有锁的线程再次尝试获取锁时,计数器都会+1)_owner:标识锁定Thread_EntryList:线程如何尝试要获取锁,它将进入_EntryList。如果成功获取锁,则_owner会变为自己;如果获取锁失败,会阻塞在同步队列EntryList和waitfor_waitSet:等待队列类似于Object的wait()和notify()相关,如果一个线程在持有的时候调用它的wait()一个锁,然后线程将进入它的_waitSet,阻塞,并释放锁。知道其他线程持有锁,调用其notify()将线程唤醒1.2JVM提供的synchronized指令通过JVM中的monitorenter指令和monitorexit指令进入和退出同步代码块。monitorenter指令编译器会将monitorenter插入到同步代码块的开头。执行该指令时,会尝试获取同步对象的monitor对象的所有权(尝试获取锁)monitorexit指令编译器会在同步代码块末尾或异常处插入monitorexit,释放所有权同步对象的monitor对象(释放锁)1.3MarkWord对象由三部分组成——对象头,实例数据,对齐填充对象头结构如下(非数组2个字,数组3个字)第一个字-MarkWord第二个字-指向对象类对象的指针第三个字-sychronized数组长度的实现与MarkWord有关1.4栈帧中的锁记录线程的栈帧中有一个空间-锁记录线程如果结果获取到偏向锁,它会将偏向锁线程ID存储在锁记录中,在尝试获取轻量级锁之前,它会将对象头的MarkWord复制到自己的锁记录中。2、锁升级到JDK1.6之前,sychronzied是重量级锁。JDK1.6对锁的优化和锁的分类————无锁、偏向锁、轻量级锁、重量级锁2.1锁升级过程偏向锁持有偏向锁的线程使用后不会主动释放锁,下一个线程会尝试通过CAS获取偏向锁,将线程ID换成自己的。如果原来持有锁的线程已经结束了同步代码块,那么替换就会成功,下一个线程就会获得锁。轻量级锁持有锁的线程在使用后会尝试释放,如果释放失败则升级为重量级锁。重量级锁会阻塞不获取锁的线程。除了重量级锁,其他情况下不持有锁的线程是不会阻塞的。2.2每种锁的优缺点锁的优缺点适用场景偏向锁加锁和解锁不需要额外的消耗如果线程之间存在锁竞争,取消锁会带来额外的消耗适用于只有一个线程访问同一个在步骤代码块的情况下,轻量级锁和竞争锁线程不会阻塞,加快响应速度。如果不能使用锁,自旋会消耗CPU性能。追求响应速度。重量级锁,响应时间慢追求吞吐量,同步代码执行时间快。3.同步内存语义保证可见性。编译器会在同步代码块的开头插入monitorenter指令。同步对象的monitor(试图获取锁)的所有权,如果获取成功,则成功获取锁——JMM会将线程对应的本地内存设置为无效,让线程必须读取共享内存在执行同步代码块时从主存变量编译器会在同步代码块的末尾或异常处插入monitorexit指令。当线程执行完指令后,会释放同步对象的monitor所有权(释放锁),JMM会刷新线程本地内存中的副本保证主内存中的原子性,只允许一个线程执行一个synchronized获得锁后的代码块保证有序JMM不允许同步语句和非同步语句重新排序,但允许在同步代码块内重新排序而不改变结果(JMM虽然允许同步代码块重新排序,但在同步代码块中仍然是有序的程序员的眼睛)4.volatile和sychronizedvolatile的区别更轻,不需要加锁,不会阻塞线程。sychronized可以保证复合语句的原子性,volatile不起作用