juc - ReentrantLock源码解读(二)

上一篇介绍了不公平的重入锁,那什么是公平,什么是不公平呢?仅仅通过第一篇博客可以回答这个问题的,但是往往我们会忽略掉这个问题,看一下公屏和不公平的差别在哪里能勾引起我们更多的思考,这样就能解释什么是公平、非公平了。(在继续看这篇博客之前,请一定要读一下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中)。

好了,很简单的就完成了公平锁的研究。。。。

猜你喜欢

转载自suichangkele.iteye.com/blog/2368176