ReentrantLock可重入锁、公平锁非公平锁区别与实现原理

简介: ReentrantLock可重入锁、公平锁非公平锁区别与实现原理

ReentrantLock是lock接口的一个实现类,里面实现了可重入锁和公平锁非公平锁

ReentrantLock公平锁和不公平锁实现原理

公平锁会获取锁时会判断阻塞队列里是否有线程再等待,若有获取锁就会失败,并且会加入阻塞队列

非公平锁获取锁时不会判断阻塞队列是否有线程再等待,所以对于已经在等待的线程来说是不公平的,但如果是因为其它原因没有竞争到锁,它也会加入阻塞队列

进入阻塞队列的线程,竞争锁时都是公平的,应为队列为先进先出(FIFO)

默认实现的是非公平锁

public ReentrantLock() {
    //非公平锁
    sync = new NonfairSync();
}

还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁

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

非公平锁

非公平锁获取锁nonfairTryAcquire方法,对锁状态进行了判断,并没有把锁加入同步队列中

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        final void lock() {
            //比较当前状态 如果持有者是当前线程
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                //如果不是 尝试获取锁
                acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
            //获取锁
            return nonfairTryAcquire(acquires);
        }
    }
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;
        }

公平锁

代码和nonfairTryAcquire唯一的不同在于增加了hasQueuedPredecessors方法的判断

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
       protected final boolean tryAcquire(int acquires) {
           //获取当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
            //判断当前对象是否被持有
            if (c == 0) {
                //如果等待队列为空 并且使用CAS获取锁成功   否则返回false然后从队列中获取节点
                if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {
                    //把当前线程持有
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
           //若被持有 判断锁是否是当前线程  可重入锁的关键代码
            else if (current == getExclusiveOwnerThread()) {
                //计数加1 返回
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            //不是当前线程持有 执行
            return false;
        }
    }

acquire()获取锁

public final void acquire(int arg) {
        //如果当前线程尝试获取锁失败并且 加入把当前线程加入了等待队列 
        if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //先中断当前线程
            selfInterrupt();
    }

关键代码

就是tryAcquire方法中hasQueuedPredecessors判断队列是否有其他节点

public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

可重入性实现原理

在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功

由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功

1、获取锁方法

protected final boolean tryAcquire(int acquires) {
           //获取当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
            //判断当前对象是否被持有
            if (c == 0) {
              //...略
            }
           //若被持有 判断锁是否是当前线程  可重入锁的关键代码
            else if (current == getExclusiveOwnerThread()) {
                //计数加1 返回
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            //不是当前线程持有 执行
            return false;
        }

每次如果获取到的都是当前线程这里都会计数加1

释放锁

protected final boolean tryRelease(int releases) {
    //每次释放都减1
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //等于0才释放锁成功
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

如果锁被获取n次,也要释放了n次,只有完全释放才会返回false


相关文章
|
4月前
ReentrantLock 可重入锁总结
ReentrantLock 可重入锁总结
37 0
|
6月前
|
Java
ReentrantLock(可重入锁)源码解读与使用
ReentrantLock(可重入锁)源码解读与使用
|
存储 安全 Java
aqs原理初探以及公平锁和非公平锁实现
aqs原理初探以及公平锁和非公平锁实现
302 0
|
算法
ReentrantLock 是如何实现可重入性的?
ReentrantLock 是如何实现可重入性的?
59 0
|
安全 Java
并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition
并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition
95 0
并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition
ReentrantLock是如何实现可重入性
ReentrantLock是如何实现可重入性
70 0
|
Java API 调度
图解ReentrantLock公平锁和非公平锁实现
图解ReentrantLock公平锁和非公平锁实现
141 0
图解ReentrantLock公平锁和非公平锁实现
Java多线程 -- 公平锁和非公平锁的一些思考
在java的锁机制中,公平和非公平的参考物是什么,个人而言觉得是相对产生的结果而立,简单的来说,如果一个线程组里,能保证每个线程都能拿到锁,那么这个锁就是公平锁。
1359 0
|
测试技术 API
Juc并发编程04——可重入锁、公平锁与非公平锁
1.ReentrantLock使用介绍 之前我们一直使用的Lock实例都用的是ReentrantLock,实际上,这是一种可重入锁。简单来说,就是对同一个线程可以进行多次的加锁操作
|
Java
彻底理解ReentrantLock可重入锁的使用
java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。这篇文章主要是从使用的角度来分析一下ReentrantLock。
203 0
彻底理解ReentrantLock可重入锁的使用