aukocharlie synchronized 是关键字 , 是 Java 亲儿子 , class 文件里面甚至为 synchronized 留有单独的标识 , 对象头里面关于锁的信息就是给 synchronized 优化用的 , 而 AQS 只是一个关于锁的一个工具罢了 , 你也可以把它的代码复制一份叫 xiao AQS , 照样用(doge 说回来 , synchronized 确实是基于操作系统 , JIT后的汇编码有 lock comxchg 之类的指令
johnbanq 貌似底层就是借助了操作系统,AQS是基于LockSupport这个有native方法的类实现线程阻塞与唤醒。 另,Doug Lea早年实现AQS的时候是写了篇论文的,看看也许对研究AQS有所帮助#LearnFromTheAuthor
xiao 谢谢!刚看了论文,这里用的是park来阻塞线程,如果park底层也是操作系统层级的,那这里感觉aqs和synchronized的主要区别就在于:aqs是自己在java层面实现的阻塞队列,而synchronized是借助操作系统实现的阻塞队列,不知道可否这样理解
johnbanq 也可以,synchronize是JVM级别的锁原语,如果是重量级锁状态的话是基于操作系统的锁实现的,锁的等待队列由操作系统管理。AQS应该把等待队列拉出来自己做,只是借用了操作系统的API阻塞和解除阻塞线程来暂停执行,等待锁可用而已。
xiao https://concurrency-interest.altair.cs.oswego.narkive.com/Bx94AChl/why-is-reentrantlock-faster-than-synchronized 谷歌了一下,这里有一个链接,doug lea曾经亲自回答了这个问题,事实上aqs更快的原因还是在于,aqs在实现的时候相比于synchronized做了很多的优化,而且本身设计Lock的初衷也是在于提供更高的灵活性,在此基础上对性能做了优化
aukocharlie 最近在看 HotSpot,有了更深刻的理解 AQS 和 synchronized 都是用的 park (这里只讨论 posix 实现) AQS 使用的 LockSupport.park() 最终调用的是 JavaThread->parker()->park(),其中 parker() 返回 Parker 类型指针,JavaThread 继承 Thread,Parker 继承 os::PlatformEvent synchronized 调用的是 Thread->_ParkEvent->park(),其中 _ParkEvent 是 ParkEvent 类型指针,ParkEvent 也是继承 os::PlatfomEvent os::PlatformEvent 有 park() 方法,且 Parker 对 park 方法重写了 park 方法都是用 pthread 的条件变量来实现 在上面的前提下,去看它俩的区别 AQS 的 unpark 时机由上层把控,synchronized 则由 VM 把控。所以我现在觉得它们效率上差距其实很有限,就看哪个优化得好。比如 synchronized 的等待队列(叫CXQ)中的第一个等待的线程会以 1ms, 8ms, 64ms... 这样简单的退避算法来 park,这里假设 AQS 用的是频率更高的退避算法,自然会发现 synchronized 貌似更慢些,当然这只是假设,AQS 具体怎么优化我没去比较 正是由于 AQS 的 unpark 由上层控制,所以条件变量的 Spurious wakeup 问题也得自己解决,LockSupport.park() 的 Java doc 也说了 The call spuriously (that is, for no reason) returns. ;而 synchronized 的 Spurious wakeup 则由 VM 控制住了,不会对程序产生影响。 这个区别产生的原因,就是上面提到的 Parker 对 park 方法重写了。原本 os::PlatformEvent::park() 中 pthread_cond_wait() 外面用的是 while 包着的,而 Parker 重写后的 park 方法则改成了用 if 包着,就这样把 Spurious wakeup 问题抛给了上层