为啥synchronized无法禁止指令重排,但可以保证有序性?加了锁之后,只能有一个线程获得到了锁,获得不到锁的线程就要阻塞。所以同一时间只有一个线程执行,相当于单线程,而单线程的指令重排是没有问题的。
还有就是在进入synchronized代码块会先添加一个屏障,在最后释放屏障。保证同步代码块中的代码不能和同步代码块外面的代码进行指令重排,在其内部还是会发生指令重排但基本不会影响结果
synchronized可以保证有序性但是不能避免指令重排,在双重检验的单例模式中,必须加volatile来避免因为发生指令重排,返回错误实例
synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit 指令,其中
monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结束位
置。
当执⾏ monitorenter 指令时,线程试图获取锁也就是获取 对象监视器 monitor 的持有权。
在 Java 虚拟机(HotSpot)中,Monitor 是基于 C++实现的,由ObjectMonitor实现的。每个对
象中都内置了⼀个 ObjectMonitor 对象。
另外, wait/notify 等⽅法也依赖于 monitor 对象,这就是为什么只有在同步的块或者⽅法
中才能调⽤ wait/notify 等⽅法,否则会抛出 java.lang.IllegalMonitorStateException 的异常的
原因。
在执⾏ monitorenter 时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取
后将锁计数器设为 1 也就是加 1。
在执⾏ monitorexit 指令后,将锁计数器设为 0,表明锁被释放。如果获取对象锁失败,那当前
线程就要阻塞等待,直到锁被另外⼀个线程释放为⽌。
synchronized 修饰的⽅法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是
ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法。JVM 通过该
ACC_SYNCHRONIZED 访问标志来辨别⼀个⽅法是否声明为同步⽅法,从⽽执⾏相应的同步调
⽤
总结
synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit 指令,其中
monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结束位
置。
synchronized 修饰的⽅法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是
ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法。
不过两者的本质都是对对象监视器 monitor 的获取