ReentrantReadWriteLock也是基于AQS来实现的,相比synchronized和ReentrantLock这种完全互斥锁。
它更加适合这种多读少写的场景。因为并发读是不会造成一个安全问题的。
那它底层是如何实现的呢?
首先依旧需要靠AQS的核心state属性,int类型的,只不过,因为我们现在有两把锁,一把读锁一把写锁,所以我们要把int拆开来用,那么在java中,int是32位的,我们将低16位用来记录写锁,高16位用来记录读锁。
写操作进来,就修改低16位的值,读操作进来就修改高16位的值。
那么由于ReentrantReadWriteLock是一个可重入锁,这个时候就会出现一个问题,我不知道哪个线程重入了。
比如ABC三个线程,但是我这里发现锁的次数是4,那么我如何找到是哪个线程重入了呢?
我们借助ThreadLoacal来解决,线程获取到锁之后,不仅要修改state,还要在自己的ThreadLocal中记录重入的次数。
那么解决了这个问题之后,我们再来看一个问题,如果我现在要写的话,那么我就需要等待读操作都执行完,此时如果读的并发非常高,一直有读操作进入,我就没法写了?这个就是写饥饿问题。
解决的方式是队列,我在读之前,先进队列找,是否有写操作在排队,如果有,那么我也进入队列排队。当写操作执行完了,那么读操作就继续飞速并发执行,直到下一个写操作的到来。相当于在写的时候我们稍微串行一下,降低了部分时间段的并发效率,保证了安全性。