来源:blog.csdn.net/randompeople/article/details/114917087为什么javawait/notify必须和synchronized一起使用?我也是基于这个困惑来理解原因。什么是同步Java为同步提供了两种基本语义:同步方法和同步块,看一个demo:}\\2.同步块publicvoidsyncBlock(){synchronized(this){System.out.println("helloblock");具体需要区分:修改实例方法,作用于当前实例Locking,在进入同步代码之前获取当前实例的锁。访问不同的实例对象不会形成锁。修饰的静态方法,作用于当前类对象的锁。在进入同步代码之前,先获取当前类对象的锁。修改代码块,指定锁对象,给定对象加锁,在进入同步代码库Lock之前获取给定对象的锁。它的特点:原子性、可见性、有序性、可重入性、synchronized是如何实现锁的。看来synchronized实现的锁是基于类对象实现的。让我们来看看如何实现它们。它实际上与类对象有关。对象头共同作用,对象在内存中的布局分为三个区域:对象头、实例数据和对齐填充。对象头中有一个MarkWord,主要存储对象的hashCode、锁信息、分代年龄或GC标志等信息。可能的情况罗列如下:synchronized与lockflag共同实现锁。主要分析重量级锁,通常称为同步对象锁。锁标志为10,指针指向监视器对象(也称为监视器或监视器锁)的起始地址。每个对象都有一个与之相关联的监视器,并且有许多方法可以实现对象与其监视器之间的关系。例如,监视器可以与对象一起创建和销毁,或者在线程尝试获取对象锁时自动生成。但是,当监视器一旦被线程持有时,它就会被锁定。在Java虚拟机(HotSpot)中,监视器由ObjectMonitor实现,其主要数据结构如下(位于HotSpot虚拟机源代码的ObjectMonitor.hpp文件中,用C++实现):ObjectMonitor(){_header=NULL;_count=0;//记录数_waiters=0,_recursions=0;_object=NULL;_owner=NULL;_WaitSet=NULL;//等待状态的线程会加入_WaitSet_WaitSetLock=0;_Responsible=NULL;_succ=NULL;_cxq=NULL;FreeNext=NULL;_EntryList=NULL;//处于等待锁块状态的线程会被加入到链表中_SpinFreq=0;_自旋时钟=0;所有者线程=0;}上面有两个字段很重要:_WaitSet队列中处于等待状态的线程会被加入到_WaitSet中。_EntryList队列中等待锁块的线程将被添加到列表中。_owner_owner指向持有ObjectMonitor对象的线程。下面来模拟一下进入锁的过程:1.当多个线程同时访问一段同步代码时,首先会进入_EntryList集合2.当线程获得对象的监听后,进入_Owner区域,并将监视器中的所有者变量设置为当前线程,并将监视器中的计数器计数加13。如果线程调用了wait()方法,则释放当前持有的monitor,owner变量恢复为null,count减1。同时线程进入WaitSet集合等待被唤醒。4、如果当前线程执行完毕,也会释放monitor(锁)并重置变量的值,以便其他线程进入并获取monitor(锁)wait/notify。这两个是Java对象所具有的属性,表示一种等待和通知机制。推荐一个开源免费的SpringBoot最全教程:https://github.com/javastacks/spring-boot-best-practice没有synchronized会怎样参考其他博客,看看没有synchronized会怎样,假设有2个线程,每个做2件事,T1线程代码逻辑:while(!conditionissatisfied)//line1{obj.wait();//第2行}doSomething();T2线程代码逻辑:将条件改为Satisfied;//第1行obj.notify();//line2多线程环境,没有synchronized,没有锁,可能会出现如下执行顺序:T1line1满足while条件T2line1执行T2line2执行,notify发出T1line2执行,wait再次执行。这个执行顺序导致发出notify通知,但是没有用。wait是后面执行的,所以有人说不保证原子性,就是line1和line2一起执行。情况也是如此。称为丢失唤醒问题。解决办法是使用synchronized来加锁,于是有人写了这段代码:synchronized(lock){while(!条件满足){obj.wait();}doSomething();}synchronized(lock){改变条件以满足;obj.notify();}这样就靠锁来达到目的了。但是这段代码会造成死锁,因为T1先wait(),然后T2notify();问题是T1在持有锁后被阻塞了,而T2一直无法获取到锁,所以永远无法在notify()和T1的阻塞状态解除时,与T1形成死锁。因此,JVM在实现wait()方法时,必须先隐式释放锁,然后阻塞,并且在被notified()之后,从wait()方法返回之前,可以在执行之前隐式重新获得锁用户代码可以继续。为此,需要为obj.wait()方法提供一个锁引用,否则obj.wait()不知道无形中释放哪个锁,所以调整后的结果如下:synchronized(lock){while(!conditionissatisfied){obj.wait(lock);//obj.wait(lock)伪实现//[1]unlock(lock)//[2]阻塞自己,等待notify()//[3]已被通知(),重新锁定(lock)//[4]obj.wait(lock)方法返回成功}doSomething();}[最终形式]把lock和obj组合成一个另外的线程API比如PThread提供wait()函数签名类似于cond_wait(obj,lock),因为同一个锁可以管理多个obj条件队列。Java自带的锁和条件队列是1:1的关系,所以直接用obj作为锁。所以这里不需要额外提供锁,直接使用obj,代码更简洁:synchronized(obj){while(!条件满足){obj.wait();}doSomething();}synchronized(lock){改变条件满足;obj.notify();}lostwakeupwait/notify如果不结合synchronized会造成lostwakeup,wait的线程很难唤醒,单独使用会有问题。近期热点文章推荐:1.1000+Java面试题及答案(2022最新版)2.厉害了!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!
