编程技术分享平台

网站首页 > 技术教程 正文

Java多线程(1)——Lock与Synchronizer的恩怨情仇

xnh888 2024-10-30 04:43:30 技术教程 42 ℃ 0 评论

下面我们从一些源码的角度来看看JUC包中lock锁的经典实现ReentrantLock,并且看看AbstractQueuedSynchronizer(AQS,下面就以该简称叙述了)同步器的底层实现。

本篇内容较多,希望大家耐心看完,比较难,不是很好理解,如有错误的地方,望大家海涵。标题只是为了吸引眼球,但两者确实有着千丝万缕的关系。因为想要搞明白Java的重入锁ReentrantLock,你必须要明白AQS;要想搞明白AQS,ReentrantLock锁的辅助分析又必不可少,所以我就两个一起看了。

Lock和synchronized的区别

在我们之前学习synchronized时,我们也说到,在JDK1.6之前效率是很低的,但是和LOCK相比较,还是有着很大的差距;在1.6之后做了很大的优化和提升(锁优化升级),相比较而言,性能上和LOCK相差无几;

虽然synchronized隐式地帮助我们实现了我们想要的锁的功能(不用我们自己操心如何上锁、解锁),但是缺少了上锁和解锁的可操作性,导致一些问题的不可控,且它为独占式的锁,在真正的高并发场景是满足不了我们的需求的;而Lock支持中断和超时、还支持尝试机制获取锁,提高了可控性和可操作性; 且在JUC包下的有着很多锁的实现,可以在不同的需求场景应用合适的锁。

下面就跟着笔者,打开工具idea/eclipse来进入源码世界吧,具体源码位置如下图:

从类关系UML图大概了解AQS和Lock的关系

从上图中我们可以看到,这里有两个顶级接口,Lock和Condition,上源码

Lock就是锁的顶级父类,定义了几个加锁和释放锁的方法,还有一个就是可以新建条件等待队列的方法newCondition();

Condition按照我的理解它代表某个条件,意思就是如果某个条件还没达成,就会建立一个等待条件达成的等待队列,这个等待队列里放的都是些等待这个条件可行的线程。观其定义的一些方法,就跟我们之前篇幅里讲wait/notify范式类似,其实他们的原理都是相通的。其中的await就相当于wait(),signal()/signalAll()相当于notify()/notifuAll()。我们后面都会去分析的。

再回到上面的UML图,刚刚我们只是简单介绍了下两个顶级父接口,这里我们还有一个抽象类AbstractOwnableSynchronizer,他是我们主角AQS的父类。如下

这个类中只定义了一个属性exclusiveOwnerThread(当前独占模式所有者,本质是一个线程,独占模式什么意思后续会说),其他什么也没有,为什么就这么一个属性要单独搞个父类呢,我们观其父类的子类其实是有两个,还有一个AbstractQueuedLongSynchronizer,其实这个类就是AbstractQueuedSynchronizer的一个补充实现,就是里面的资源state变量,一个是int型,还有一个是long型而已。。。一般情况下,用不到哈~

再继续,我们看我们的主角AbstractQueuedSynchronizer类,观其类名就应该知道,这个类大概就是抽象的同步队列,既然是队列,那意思就是说,我们这个类里面维护了一个队列,学过数据结构的我们都知道,队列的建立一般都需要节点Node,这时我们再看UML图,在我们AQS的内部确实实现了一个Node的类。

这个节点类呢其实就是用来包装线程的,他给出了进入队列线程的一些额外属性,比如当前的状态,它前面的线程节点,后面的线程节点等等。由此我们看出,AQS它就是一个用来管理同步线程队列的一个类。

再继续,我们看到了Lock的一个实现类(另一个主角),重入锁ReentrantLock,观其内部,一共继承AQS实现了三个类,一个Sync,还有两个继承了Sync,分别是NonfairSync(非公平)和FairSync(公平),其实这两个类就是我们之前讲过的公平和非公平锁的体现了。

由于内容和篇幅较长,我们明天会继续讲解这个内容哦!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表