1.锁的本质(举个例子)
停车场的车位需要买,买了后才能停车,这个规则相当于加锁。而买到了车位后,可以停车,这相当于抢锁成功。而在编程里,当我们有个资源,有多个线程需要去争抢它,有线程安全问题,那么就需要先获得锁,那么就是lock,加锁,即加规则,抢到锁后,就是获得了访问这个资源的资格,即抢锁成功,执行任务。
2.Lock和ReadWriteLock接口的结构及关系
从图中可以看出ReentranLock实现了Lock接口,ReentrantReadWriteLock实现了Lock和ReadWriteLock接口。
关于Lock接口相关的方法,读者可以自行去了解。说到Lock,不得不提Condition这个类,如果说,Lock和synchronized是一对的话,那么Condition就相当于Object,只是Condition是和Lock接口的相关实现类一起使用,读者也可以自行了解。
3.重点介绍一下ReentranLock(可重入锁)
从代码的角度来说,就是可以加一次锁,不解锁还可以再加锁。
其概念:有两个重要的属性owner和count
a.owner当前这个可重入锁,它被哪个线程占用,如果它没有占用则是null,如果占用了,则存的是当前线程的引用。
b.当我们调用lock方法后,加锁成功,owner变成主线程的引用 ,count变成1。
c.再加一次锁,count值加1,从1变成2。
d.unlock,并不会将锁释放掉,但lock一次,count值会由2变成1.
e.再unlock解锁一次,这个时候,锁才会被真正地释放掉。owner变成null,count变成0。
注意:加锁的次数和解锁的次数必须相同。
ReentranLock原理:
尝试抢锁:模拟多个线程抢锁的一个过程,线程分别用t1,t2,t3,t4来标注,用lock方法来抢锁。这里的waiters是一个等待队列。
有4个线程来抢锁,首先,判断count是否为0,如果是0,说明锁没有被占用,需要抢锁,用CAS(0,1)来抢锁,
如果t1抢锁成功,则owner会存放t1线程的引用,count值变成1,
其它的线程判断count不为0,则抢锁失败,便进入等待队列。尝试释放锁:如果owner存放的是当前线程引用,那么,count值减1,并重置新的count值,如果count值变成0,则说明锁被成功释放。
用lock抢锁的时候,会用CAS实现自旋,具体的逻辑可以看代码:
关于可重入锁的就介绍这里,大家可以思考一下,相比于synchronizedt,lock接口与它有什么区别,及优缺点。
还有可重入锁有什么问题?想了解更多,记得关注,后续有更多的知识更新。