美文网首页程序员计算机技术
Java并发-重入锁ReentrantLock

Java并发-重入锁ReentrantLock

作者: 油多坏不了菜 | 来源:发表于2019-02-28 19:41 被阅读3次

    基础

    • 重入锁:一个线程对同一个锁资源可以重复获取(如synchronized关键字)
    • ReentrantLock是可重入锁,并且支持获取锁时的公平与非公平选择。
    • ReentrantLock默认为非公平锁,这是出于性能考虑(对于非公平锁,一个刚释放锁的线程很大概率可以再次获得该锁,而不用去排队,减少上下文切换次数)。而公平锁总是会让你到同步队列走一遭,即使你刚释放了锁,然后立马请求锁。即公平锁的实现是以线程切换为代价的。

    基本使用

    ReentrantLock lock = new ReentrantLock();//非公平锁
    //ReentrantLock lock = new ReentrantLock(true);//公平锁
            lock.lock();
            try{
                //to do
            }finally {
                lock.unlock();
            }
    

    ReentrantLock的基本骨架

    ReentrantLock implements Lock{
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer {......};
    static final class NonfairSync extends Sync {};//非公平锁用这个同步器
    static final class FairSync extends Sync {};//公平锁用这个同步器
    }
    public ReentrantLock(boolean fair) {//选择是公平锁还是非公平锁
         sync = fair ? new FairSync() : new NonfairSync();
    }
    public ReentrantLock() {//默认为非公平锁
            sync = new NonfairSync();
    }
    
    

    源码角度分析其可重入性和公平性的实现

    可重入特性实现(非公平锁为例)

    锁的获取

    如果当前同步状态已经被线程获取(state != 0),判断当前尝试获取同步状态的线程是否是已经获取同步状态的线程,如果是就代表当前线程重入获取锁,将同步状态的值增加(所以获取锁多少次,就应该释放锁多少次),返回成功。

     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;
            }
    
    锁的释放

    因为同步状态的获取可能获取了n次,所以应该只有最后一次释放之后才应该返回true(线程已释放锁)。

    protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }
    
    

    公平性实现

    用户层面而言:锁的获取严格遵守FIFO模式
    源码角度来说:公平锁的tryAcquire与非公平锁不同之处在于---公平锁如果判断锁空闲,还需要去判断同步队列中有没有在排队的(非公平锁没有这一步),如果有的话就去同步队列中排队。

    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;
            }
    

    相关文章

      网友评论

        本文标题:Java并发-重入锁ReentrantLock

        本文链接:https://www.haomeiwen.com/subject/nxbkuqtx.html