前言:

最近重读java并发编程实战,又读到了wait()和notify()必须在synchronized块中使用这一段,当时没有看懂,这次重新思考一下这个问题。

转载:

首先贴上搜到的资料:

Java的LockSupport.park()实现分析

Java的wait()、notify()学习三部曲之一:JVM源码分析

深入剖析基于并发AQS的(独占锁)重入锁(ReetrantLock)及其Condition实现原理 Java虚拟机对synchronized的优化

正文:

第一次思考这个问题的时候,觉得wait方法和synchronized是两个毫不相关的东西,觉得synchronized是用来给代码加锁的,wait和notify是控制线程的执行暂停的,他们俩根本就是雷锋和雷峰塔嘛。。。

这次看AQS代码的时候,发现了park和unpark这两个方法,再加上看了转载的第一篇帖子Java的LockSupport.park()实现分析里边对两者的描述,忽然明白了两者的联系:

wait和synchronized 都是控制线程执行和暂停的方式,只是wait是我们主动控制线程暂停,synchronized 线程是被动暂停。为了保证资源的独占性,必须让其他线程暂停。所以在适当的时候让合适的线程暂停就能实现线程的同步。

让线程暂停可以使用一个while(true)循环,在循环内部检测某个条件,但是这样的话会浪费大量的时间片执行空循环。所以操作系统提供了让线程让出时间片的系统调用,比如unix里的pthread_join、pthread_exit以及semaphore和mutex。

java里边的park和unpark就类似于这样的调用,可以让程序控制线程的运行状态。而synchronized是对这两个方法更高级的抽象,不需要我们手动调用底层的方法就能实现同步,从转载的第二篇文章Java的wait()、notify()学习三部曲之一:JVM源码分析里也能看到,synchronized最终让线程阻塞就是调用的park方法,离开同步块的时候调用的是unpark方法。

最后的wait和notify猜测的话,因为synchronized一旦持有了锁,不能被中断,如果出现死锁就永远解不开了,所以才有了wait这种可以响应中断的方法来丰富synchronized的功能。因为不好用,所以后来干脆把park和unpark封装成nativeAPI,在此基础上封装出了AQS,并用AQS实现了ReetrantLock,来代替synchronized。

在最后一篇转载中提到java对synchronized做了很多优化,比如:偏向锁、锁消除等等,在第一篇中也有提到,大概意思是说如果直接调用park和unpark会发生系统调用,这些优化就是通过各种方式减少这两个方法的调用。回想一下AQS代码这么复杂,核心的目的好像也是减少这两个方法的调用次数(比如node的share模式对应偏向锁)。

总结:

记得看其他人的文章,开头或者结尾总是要把参考资料附在上边。我思考了一下,觉得在网上发表文章,一种是作者高屋建瓴,能够写出特别专业的介绍和解读,这种的文章觉得好,转载就好了,自己写的不如别人是因为自己还没有到达那个水平,不用觉得受打击。另一种文章,旁征博引但是不知所云(比如我写的这个)是因为菜鸟都有一个练习的过程,遇到这种的文章,也不用觉得烦,好好思考,能够总结出自己的结论,因为别人看了我写的东西,说不定也是一阵蛋疼[哭笑]。

最后,让我们保持独立思考,不卑不亢。长成自己想要的样子!

(引用自 我非常喜欢的B站up主 」独立菌儿「的口头禅哔哩哔哩 ( ゜- ゜)つロ 干杯~ Bilibili)
推荐阅读:
相关文章