juc - ReentrantLock源码解读(二)
程序员文章站
2022-04-19 08:23:22
...
上一篇介绍了不公平的重入锁,那什么是公平,什么是不公平呢?仅仅通过第一篇博客可以回答这个问题的,但是往往我们会忽略掉这个问题,看一下公屏和不公平的差别在哪里能勾引起我们更多的思考,这样就能解释什么是公平、非公平了。(在继续看这篇博客之前,请一定要读一下http://suichangkele.iteye.com/blog/2368173 这个博客)。
在ReentrantLock的构造方法中,如果没有设置参数,默认就是不公平的,如果设置为true,则会生成一个FairSync的同步器,我们看一下他和NonFairSync的差别的地方吧:
static final class NonfairSync extends Sync {//不公平的 private static final long serialVersionUID = 7316153563782823691L; final void lock() {//在调用lock的时候上来就会查看当前的标记是否是0, if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires);//这个方法也是先看标记是不是0,如果是的话就会调用compareAndSetState(0,1),尽管此时可能有阻塞的线程在队列中。 } } static final class FairSync extends Sync {//公平的 private static final long serialVersionUID = -3000897897090466540L; final void lock() {//在调用lock的时候不会查看标记是不是0,而是直接调用acquire,也就是先调用tryAcquire(acquire中就是先调用的tryAcquire方法) acquire(1); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {//这个地方就是关键点,如果当前的锁没有被占用(此时可能有排队等待的线程), if (!hasQueuedPredecessors()//公平和不公平就体现在这里,公平的会看当前的队列中是否存在已经排队的线程,如果有的话就不会捕获锁,尽管锁当前没有被占有,如果没有等待者,才会捕获锁。 compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }
看完上面的代码可能大概的意思就懂了:由于这一种情况的存在,即:占有锁的线程释放了锁,但是被队列中等待的线程捕获锁之前,又有新的线程来获得锁,它可能会比队列中的线程领先获得锁。在这种情况里,就体现了公平非公平。如果是公平的,那么新的线程就会检查队列中是否已经存在了等待的线程,也就是FairSync的tryAcquire方法中hasQueuedPredecessors的判断,如果是,那新的线程就不能获得锁,将锁让给已经等待的线程。如果是非公平的,即在新线程获得锁时不关心是否存在等待的线程,直接尝试调用compareAndSetState(0, 1)方法,也就是获得锁的方法,此时如果获得了锁,尽管之前刚刚释放锁的线程已经唤醒了等待的线程,等待的线程会继续进行for循环,进入到第一个if判断中,但是没有tryAcquire成功,所以就会继续被挂起(这段代码在http://suichangkele.iteye.com/blog/2368173中)。
好了,很简单的就完成了公平锁的研究。。。。