Java并发系列-ReentrantLock学习笔记

ReentrantLock学习笔记

内部锁形式

  • 公平锁 FairSync,新的被阻塞的线程必须排在旧的被阻塞的线程后面抢到锁
  • 非公平锁 NonfairSync 新的被阻塞的线程可能会抢到锁

默认锁形式

​ 默认非公平锁,可以通过有残构造方法切换成公平的,但是公平锁的性能肯定是没有非公平锁的性能好的

public ReentrantLock() {   
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

内部抽象锁Sync

​ 由于ReentrantLock是独占锁,所以不管是公平锁还是非公平锁,释放锁的方式是相同的,因为公平性说的只是在获取锁的时候,所以Sync实现了尝试释放锁tryRelease的方法,不同公平性锁的尝试获取锁方法tryAcquire由子类具体实现,这是标准的模板方法模式。另外定义了加锁方法lock,因为加锁之前肯定要先获得锁,所以该方法是一个抽象方法

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        
        abstract void lock();
        //非公平的尝试获取锁
        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()) {
                //多次加锁,state值增加固定值,说明了ReentrantLock是一个可重入锁
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        protected final boolean tryRelease(int releases) {
            //ReentrantLock的可重入性导致多次释放锁就多次减去相同的值
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            //由于Thread.currentThread() != getExclusiveOwnerThread() 判断的存在,所以不需要考虑线程安全问题
            setState(c);
            return free;
        }
        
        //..........................................
        
    }

公平锁FairSync

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            //公平锁的获取不会去判断state的状态,获取锁只能依赖tryAcquire的逻辑,所以公平性由tryAcquire保证
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //核心点hasQueuedPredecessors(),判断当前线程前面还有没有其他线程在排队,如果有返回fasle,如果没有执行获取锁的操作
                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;
        }
    }

非公平锁NonfairSync

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    //因为是非公平锁,所以新的阻塞节点可以和已经排队的阻塞节点一起竞争锁,而ReentrantLock又是独占锁,state状态为0说明当前所没有被持有,所以不管是新的阻塞点还是旧的阻塞点,只要state状态等于0,就可以尝试去抢占锁
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //调用AQS的方法
            acquire(1);
    }
    
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

总结:

​ 这个源码其实挺简单的,总结如下:

  • 公平非公平的实现差别只是再或许锁之前判断一下当前线程前面还有没有现在在等待,如果有就返回fasle 如果没有就成功获取锁,另外一个就是公平模式下获取锁必须依赖tryAcquire方法。
  • ReentrantLock是可重入独占锁,加锁state值加1,解锁就减1,为0就表明没有锁是空闲的(很像jvm的引用计数法)
  • ReentrantLock默认非公平锁,可以通过构造参数指定

猜你喜欢

转载自www.cnblogs.com/lx-lixiao/p/12149031.html