美文网首页
java并发包之ReentrantLock

java并发包之ReentrantLock

作者: zoengyunhing | 来源:发表于2017-12-01 18:58 被阅读0次

    一、前言

    在上文中谈到了AQS是Lock实现的前提,而本文说到的ReetrantLock就是在此基础上处理的。而本文中会对ReetrantLock公平性,可重入性等进行介绍。

    二、特性

    2.1 可重入

    可重入性也就是能够让线程多次进行锁的获取操作,首先举个例子:

    public class ReentranDemo {
    
        private ReentrantLock lock = new ReentrantLock();
    
        public void putDemo(){
            try {
                lock.lock();
                System.out.println("putDemo"+Thread.currentThread().getName());
                reInDemo();
            }finally {
                lock.unlock();
            }
        }
    
        public void reInDemo(){
            lock.lock();
            try {
                System.out.println("reInDemo"+Thread.currentThread().getName());
            }finally {
                lock.unlock();
            }
        }
         public static void main(String[] args) {
                new Thread(){
                    public void run(){
                        new ReentranDemo().putDemo();
                    }
                }.start();
        }
    }
    
    

    运行结果

    putDemoThread-0
    reInDemoThread-0
    
    

    如果ReentrantLock不是可重入锁的话,那么该线程会进入死锁。

    2.2 公平性

    公平锁:线程获取锁资源的顺序为先后调用lock方法的顺序依次获取锁
    非公平锁:加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待

    看个例子:

    public class FairLockDemo {
    
        private static Lock fairLock = new ReentrantLock(true);
        public void fair() {
            for (int i = 0; i < 5; i++) {
                new Thread(){
                    public void run(){
                        for (int i = 0; i < 5; i++) {
                            fairLock.lock();
                            try {
                                System.out.println(Thread.currentThread().getName() + "is locking " );
                            } finally {
                                fairLock.unlock();
                            }
                        }
                    }
                }.start();
    
            }
    
        }
        public static void main(String[] args) {
            FairLockDemo fairLockDemo = new FairLockDemo();
            fairLockDemo.fair();
        }  
    }
    

    返回结果如下:

    Thread-0is locking 
    Thread-1is locking 
    Thread-2is locking 
    Thread-3is locking 
    Thread-0is locking 
    Thread-4is locking 
    Thread-1is locking 
    Thread-2is locking 
    Thread-3is locking 
    Thread-0is locking 
    Thread-4is locking 
    Thread-1is locking 
    Thread-2is locking 
    Thread-3is locking 
    Thread-0is locking 
    Thread-4is locking 
    Thread-1is locking 
    Thread-2is locking 
    Thread-3is locking 
    Thread-0is locking 
    Thread-4is locking 
    Thread-1is locking 
    Thread-2is locking 
    Thread-3is locking 
    Thread-4is locking 
    

    如例子所示,连续获取的情况基本没有

    2.3 非公平锁

    加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待

    看个例子:

    public class UnfairLockDemo {
    
        private static Lock unfairLock = new ReentrantLock();
    
        public void unfair() {
            for (int i = 0; i < 5; i++) {
                new Thread(){
                    public void run(){
                        for (int i = 0; i < 5; i++) {
                            unfairLock.lock();
                            try {
                                System.out.println(Thread.currentThread().getName() + "is locking " );
                            } finally {
                                unfairLock.unlock();
                            }
                        }
                    }
                }.start();
    
            }
    
        }
    
        public static void main(String[] args) {
            UnfairLockDemo unfairLockDemo = new UnfairLockDemo();
            unfairLockDemo.unfair();
        }
    
    }
    

    返回结果如下:

    Thread-0is locking 
    Thread-3is locking 
    Thread-3is locking 
    Thread-3is locking 
    Thread-3is locking 
    Thread-4is locking 
    Thread-4is locking 
    Thread-4is locking 
    Thread-4is locking 
    Thread-4is locking 
    Thread-1is locking 
    Thread-1is locking 
    Thread-1is locking 
    Thread-1is locking 
    Thread-1is locking 
    Thread-2is locking 
    Thread-2is locking 
    Thread-2is locking 
    Thread-2is locking 
    Thread-2is locking 
    Thread-0is locking 
    Thread-0is locking 
    Thread-0is locking 
    Thread-0is locking 
    Thread-3is locking 
    

    而我们的非公平锁出现连续获取锁的情况却非常多。而出现这种情况的原因会在后文源码分析中介绍到。

    三、主要类与方法分析

    3.1 Sync

    在看源码之前我们先略微介绍下该类,这个类是继承于AQS,而Sync还有两个子类,分别是公平锁和非公平锁的两种不同的实现。

     abstract static class Sync extends AbstractQueuedSynchronizer {
    
            //具体实现由非公平和公平进行实现
            abstract void lock();
            //非公平尝试获取
            final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                //获取AQS锁资源的数量
                int c = getState();
                //如果为0表示没有线程获取资源
                if (c == 0) {
                    //利用CAS将state为0修改为当前资源数,并将当前的线程记录下来
                    if (compareAndSetState(0, acquires)) {       
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                //如果state>0且当前线程和记录的线程是同一个,则重入处理(这里就是重入锁的相应实现)
                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;
            }
            //尝试释放锁资源
            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;
            }
            //是否独占
            protected final boolean isHeldExclusively() {
                return getExclusiveOwnerThread() == Thread.currentThread();
            }
            final ConditionObject newCondition() {
                return new ConditionObject();
            }
            //获取持有该锁的线程
            final Thread getOwner() {
                return getState() == 0 ? null : getExclusiveOwnerThread();
            }
            //获取重入的线程数
            final int getHoldCount() {
                return isHeldExclusively() ? getState() : 0;
            }
    
            final boolean isLocked() {
                return getState() != 0;
            }
            private void readObject(java.io.ObjectInputStream s)
                throws java.io.IOException, ClassNotFoundException {
                s.defaultReadObject();
                setState(0); 
            }
        }
    

    nonfairTryAcquire在后文会看到非公平锁对其的调用,而内部实现的处理正是我们可重入锁的具体处理方案。

    3.2 FairSync

    接下来是我们的公平锁的类,他实现了其父类Sync的lock方法。也同时对上一篇文章AQS的tryAcquire进行了重写

    static final class FairSync extends Sync {
            //获取锁,内部主要的实现由AQS的acquire实现
            final void lock() {
                //表示会获取一个锁资源,其中一部门就是由FairSync类下的tryAcquire实现具体实现可以看下上一篇文章AQS的acquire方法
                acquire(1);
            }
            //对基类AQS的重写,表示获取资源
            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                //状态为0表示没有线程获取锁
                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;
            }
        }
    
        public final boolean hasQueuedPredecessors() {
            Node t = tail;
            Node h = head;
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }
    
    

    由于lock的主要逻辑在AQS的acquire中,而tryAcquire方法的重写,展示了Reetrantlock公平锁在获取资源与其它同步锁不同的特性。通过调用的acquire表示其是一个独占锁,而通过判断当前线程与记录线程的比较实现的是Reetrantlock可重入性。而最后判断当前节点是否有前置节点来实现了我们的公平性。

    3.3 NonfairSync

    static final class NonfairSync extends Sync {
           
            final void lock() {
                //如果能获取锁直接获取。
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    //获取不了通过nonfairTryAcquire进行锁资源获取
                    acquire(1);
            }
    
            protected final boolean tryAcquire(int acquires) {
                //sync类中有实现
                return nonfairTryAcquire(acquires);
            }
        }
    

    公平锁和非公平锁的区别:
    首先公平锁获取直接调用acquire方法,而该方法不是直接获取锁,而是调用的tryAcquire方法,而公平锁的tryAcquire首先获取state状态吐过没有线程占用(state=0),会判断在AQS的等待队列中的该节点的前置节点是否为其他同样等待的节点,如果有就说明在公平性上其他节点已经排好队了,那么会让出获取资源的权利。
    而非公平锁,一上来就直接判断state状态,完全是一种抢占的机制。如果抢占不了在调用acquire方法,而非公平的acquire中的tryAcquire同样不会像公平锁的tryAcquire方法判断节点的前置节点。

    3.4构造函数

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

    可以指定ReentrantLock的公平性

    3.5 lock方法

     public void lock() {
            sync.lock();
        }
    

    直接调用公平锁或者非公平锁的lock

    3.6 tryLock

    public boolean tryLock() {
            return sync.nonfairTryAcquire(1);
        }
    

    尝试获取锁,不会发生阻塞,成功或失败直接返回。

    3.7 unlock

    public void unlock() {
        sync.release(1);
    }
    

    释放锁直接调用AQS的release释放。

    相关文章

      网友评论

          本文标题:java并发包之ReentrantLock

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