Java并发编程——synchronize 和 ReentrantLock

1.synchronize

深入synchronized原理

2.ReentrantLock

显式锁ReentrantLock内置锁synchronize不同,它并不是一种替代内置锁的方式,而是作为一种可选择的高级功能。

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

ReentrantLock 实现了 Lock接口,意味着其具有无条件的,可中断的,可轮询以及定时的锁获取操作,所有的加锁与解锁必须显式的调用!

2.1 公平锁与非公平锁

我们先来讨论以下ReentrantLock高级功能之一:公平锁与非公平锁!:boom:
ReentrantLock 默认采用非公平锁

    public ReentrantLock() {
        sync = new NonfairSync(); //此处默认是非公平锁!
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

再看看公平锁和非公平锁的实现,两者都是实现了Sync的静态内部类。先看下公平锁的核心方法:

    /**
     * Sync object for fair locks
     */
     //公平锁!!!!!!!
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }
        //注意这个acquire方法是在AbstractQueuedSynchronizer中实现的!并不是公平锁的方法!
        public final void acquire(int arg) {
            if (!tryAcquire(arg) && //当前线程是否能获得锁!
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//获取不了加入到等待队列的尾部
                selfInterrupt(); //最终阻塞当前线程!
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState(); //获取锁计数信息
            if (c == 0) { //判断是否为0,即当前是否有线程正持有这个锁!
                if (!hasQueuedPredecessors() &&  //首先会判断是否有线程正在等待,如果有等待,为保证公平性跳到最底部返回false,即试图获取锁失败!
                    compareAndSetState(0, acquires)) { //然后通过cas操作竞争锁
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) { //ok,有线程持有这个锁,就判断一下是否是本线程持有这个锁,ok,持有,重入!
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc); //更改锁计数信息
                return true;
            }
            return false; // 都不是,直接返回false
        }
    }

再看下非公平锁的

static final class NonfairSync extends Sync {
    final void lock() {
        //  和公平锁相比,这里会直接先进行一次CAS,成功就返回了
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    // AbstractQueuedSynchronizer.acquire(int arg)
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
/**
 * Performs non-fair tryLock.  tryAcquire is implemented in
 * subclasses, but both need nonfair try for trylock method.
 */
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 这里没有对阻塞队列进行判断
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

总结:raising_hand:

  • 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。
  • 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!