JUC锁框架_ ReentrantLock原理分析

作者: wo883721 | 来源:发表于2018-03-29 09:16 被阅读63次

    上一章详细介绍了AQS的源码,这一章我们来分析JUC框架中最常用的锁ReentrantLock(可重入独占锁,也叫可重入互斥锁)

        public class ReentrantLock implements Lock, java.io.Serializable {}
    

    ReentrantLock实现Lock接口,以及Serializable可序列化接口。

    可重入独占锁的意思:

    1. 同一时间只能有一个线程持有这个锁。
    2. 持有这个锁的线程可以再次进入另一个被这个锁锁住的模块,即持有这个锁的线程可以重复获取锁。

    一. Lock接口

    package java.util.concurrent.locks;
    import java.util.concurrent.TimeUnit;
    
    public interface Lock {
    
        // 获取锁,如果获取不到,就一直等待。不响应中断请求
        void lock();
    
        // 获取锁,如果获取不到,就一直等待。如果在线程等待期间有中断请求就抛出异常
        void lockInterruptibly() throws InterruptedException;
    
        // 尝试用非公平锁方式去获取锁,立即返回。返回true表示获取成功,返回false表示获取失败
        boolean tryLock();
    
        // 在规定的unit时间内获取锁,如果时间到了还没有获取到锁,则返回false,表示获取失败
        // 如果在线程等待期间有中断请求就抛出异常
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
        // 当前线程释放占用的锁,并唤醒这个锁上的一个等待线程
        void unlock();
    
        // 创建一个Condition对象
        Condition newCondition();
    }
    

    这个接口提供获取锁,释放锁以及生成Condition对象的方法。

    二. 构造函数和成员属性

    2.1 成员属性

        // ReentrantLock通过sync变量,实现独占锁的操作。
        //Sync是AbstractQueuedSynchronizer的子类
        private final Sync sync;
    

    ReentrantLock只有这一个成员变量,它是AQS的子类,所以可以通过sync实现独占锁的操作。

    2.2 构造函数

        // 默认创建的是非公平锁
        public ReentrantLock() {
            sync = new NonfairSync();
        }
    
    
        // 根据fair值,决定创建公平锁还是非公平锁
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    

    根据参数fair来决定是公平锁还是非公平锁,默认是非公平锁。

    三. Sync内部类

    这个类也是个抽样类,它的两个子类就是FairSync和NonfairSync。有两个重要方法。

    3.1 nonfairTryAcquire方法

    使用非公平方式去尝试获取锁

        final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                // 获取锁的记录状态state
                int c = getState();
                // 如果c==0表示当前锁是空闲的
                if (c == 0) {
                    // 通过CAS原子操作方式设置锁的状态,如果为true,表示当前线程获取的锁,
                    // 为false,锁的状态被其他线程更改,当前线程获取的锁失败
                    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;
            }
    

    方法流程:

    1. 调用getState方法获取锁的记录状态c
    2. 如果c==0表示当前锁是空闲的。因为是非公平的方法获取锁,所以直接调用compareAndSetState更改锁的状态,如果成功,表示当前线程获取了锁,如果失败,表示锁的状态别的线程更改了,当前线程获取锁失败。
    3. 如果c不等于0,那么要看当前线程是不是获取锁的线程,因为ReentrantLock是可重入锁,获取锁的线程可以重复获取锁。

    3.2 tryRelease方法

    尝试释放锁资源,返回true表示完全释放了锁资源,返回false表示还持有锁资源。

    因为锁是可重入的,所以锁可能要释放多次。

        protected final boolean tryRelease(int releases) {
                // c表示新的锁的记录状态
                int c = getState() - releases;
                // 如果当前线程不是独占锁的线程,就抛出IllegalMonitorStateException异常
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                // 标志是否可以释放锁
                boolean free = false;
                // 当新的锁的记录状态为0时,表示可以释放锁
                if (c == 0) {
                    free = true;
                    // 设置独占锁的线程为null
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }
    

    方法流程:

    1. 先计算新的锁的记录状态c。
    2. 如果当前线程不是独占锁的线程,就抛出IllegalMonitorStateException异常。
    3. 当新的锁的记录状态为0时,表示完全释放锁资源,就唤醒另一个等待锁的线程了。
    4. 设置新的锁的记录状态。

    四. NonfairSync 非公平锁

        /**
         * 非公平锁
         */
        static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
    
            // 获取锁,如果没有获取到锁,则当前线程要阻塞等待
            final void lock() {
                // compareAndSetState返回true,表示当前线程获取锁成功。
                // 因为是非公平锁,所以不需要判断AbstractQueuedSynchronizer线程等待队列是否有值
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    // 调用acquire方法,获取锁
                    acquire(1);
            }
    
            // 尝试获取锁,获取到锁返回true,没有获取到返回false
            protected final boolean tryAcquire(int acquires) {
                // 调用父类的nonfairTryAcquire方法
                return nonfairTryAcquire(acquires);
            }
        }
    

    继承Sync类需要复写两个方法:

    1. lock获取锁的方法。 因为是非公平锁方式获取锁,所以先直接调用compareAndSetState方法,如果返回true,表示锁资源被当前线程持有了。返回false表示锁的状态state不是0,锁资源已经被持有了。则调用acquire方法,再次获取锁,不成功就阻塞当前线程。
    2. tryAcquire方法:尝试获取锁,获取到锁返回true,没有获取到返回false。这里调用父类的nonfairTryAcquire方法

    五. FairSync 公平锁

        /**
         * 公平锁
         */
        static final class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
    
            final void lock() {
                // 与非公平锁不同,因为它要考虑线程等待队列是否有值
                acquire(1);
            }
    
            // 尝试获取锁,与与非公平锁最大的不同就是调用hasQueuedPredecessors()方法
            // hasQueuedPredecessors方法返回true,表示等待线程队列中有一个线程在当前线程之前,
            // 根据公平锁的规则,当前线程不能获取锁。
            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                // 获取锁的记录状态
                int c = getState();
                // 如果c==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;
            }
        }
    
    1. lock 方法:与非公平锁相比,它直接调用acquire方法,因为是公平锁,所以必须考虑当前线程是不是CLH队列中第一个(即队列中第一个等待线程)

    2. tryAcquire 方法:与非公平锁相比,会调用调用hasQueuedPredecessors()方法。 hasQueuedPredecessors方法返回true,表示等待线程队列中有一个线程在当前线程之前,根据公平锁的规则,当前线程不能获取锁。

    示例

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class LockTest {
    
        public static void newThread(Lock lock, String name, int time) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程"+Thread.currentThread().getName()+" 开始运行,准备获取锁");
                    lock.lock();
                    try {
                        System.out.println("====线程"+Thread.currentThread().getName()+" 在run方法中获取了锁");
                        lockAgain();
                        try {
                            Thread.sleep(time);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } finally {
                        System.out.println("----线程"+Thread.currentThread().getName()+" 在run方法中释放了锁");
                        lock.unlock();
                    }
                }
    
                private void lockAgain() {
                    lock.lock();
                    try {
                        System.out.println("====线程"+Thread.currentThread().getName()+"  在lockAgain方法中再次获取了锁");
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } finally {
                        System.out.println("----线程"+Thread.currentThread().getName()+" 在lockAgain方法中释放了锁");
                        lock.unlock();
                    }
                }
            },name).start();
        }
    
        public static void main(String[] args) {
            Lock lock = new ReentrantLock();
            newThread(lock, "t1111", 1000);
            newThread(lock, "t2222", 1000);
            newThread(lock, "t3333", 1000);
        }
    }
    

    从这个例子中可以看出ReentrantLock是一个可重入锁,当前持有锁的线程可以进入另一个被锁住的模块,而且只有都释放完成之后,它才会去唤醒一个等待锁的线程。

    注意一个有趣点,虽然我们创建的非公平锁,但是每次释放锁完成后,去唤醒一个等待线程时,你会发现它一定是等待线程中的第一个线程。那么不是和非公平锁定义好像不一样啊。
    其实非公平锁和公平锁最大的区别是,当锁是空闲的即没有任何线程持有锁,非公平锁就允许当前线程直接获取锁,而不用考虑是否有其他线程已经在等待这把锁了。而公平锁不是这样,如果有等待线程队列,那么就将当前线程插入到等待线程队列尾。

    输出结果是

    线程t1111 开始运行,准备获取锁
    ====线程t1111 在run方法中获取了锁
    ====线程t1111  在lockAgain方法中再次获取了锁
    线程t2222 开始运行,准备获取锁
    线程t3333 开始运行,准备获取锁
    ----线程t1111 在lockAgain方法中释放了锁
    ----线程t1111 在run方法中释放了锁
    ====线程t2222 在run方法中获取了锁
    ====线程t2222  在lockAgain方法中再次获取了锁
    ----线程t2222 在lockAgain方法中释放了锁
    ----线程t2222 在run方法中释放了锁
    ====线程t3333 在run方法中获取了锁
    ====线程t3333  在lockAgain方法中再次获取了锁
    ----线程t3333 在lockAgain方法中释放了锁
    ----线程t3333 在run方法中释放了锁
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class WaitTest {
        private Lock lock;
        private Condition condition;
    
        public WaitTest() {
            this.lock = new ReentrantLock();
            this.condition = this.lock.newCondition();
        }
    
        public void waitTest() {
            lock.lock();
            try {
                System.out.println("===线程"+Thread.currentThread().getName()+" 获取了锁");
                try {
                    Thread.sleep(1000);
                    System.out.println("========线程"+Thread.currentThread().getName()+"  调用await方法 等待并释放了锁");
                    this.condition.await();
                    System.out.println("========线程"+Thread.currentThread().getName()+"  await等待被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } finally {
                System.out.println("---线程"+Thread.currentThread().getName()+"  释放锁");
                lock.unlock();
            }
        }
    
        public void signalTest() {
            lock.lock();
            try {
                System.out.println("===线程"+Thread.currentThread().getName()+"  获取了锁");
                System.out.println("-------线程"+Thread.currentThread().getName()+"  调用signal方法");
                this.condition.signal();
            } finally {
                System.out.println("---线程"+Thread.currentThread().getName()+"  释放锁");
                lock.unlock();
            }
        }
    
    }
    
    public class LockWaitTest {
    
        public static void newThread(WaitTest waitTest, String name, boolean isWait) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (isWait) waitTest.waitTest();
                    else waitTest.signalTest();
                }
            },name).start();
        }
    
        public static void main(String[] args) {
            WaitTest waitTest = new WaitTest();
            newThread(waitTest, "t1111", true);
            newThread(waitTest, "t2222", false);
    //        newThread(waitTest, "t3333", true);
    //        newThread(waitTest, "t4444", false);
        }
    }
    

    输出结果是

    ===线程t1111 获取了锁
    ========线程t1111  调用await方法 等待并释放了锁
    ===线程t2222  获取了锁
    -------线程t2222  调用signal方法
    ---线程t2222  释放锁
    ========线程t1111  await等待被唤醒
    ---线程t1111  释放锁
    

    附录

        package java.util.concurrent.locks;
    
    import java.util.Collection;
    import java.util.concurrent.TimeUnit;
    
    public class ReentrantLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = 7373984872572414699L;
        // ReentrantLock通过sync属性,实现独占锁的操作。
        // Sync是AbstractQueuedSynchronizer的子类
        private final Sync sync;
    
    
        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();
                // 获取锁的记录状态state
                int c = getState();
                // 如果c==0表示当前锁是空闲的
                if (c == 0) {
                    // 通过CAS原子操作方式设置锁的状态,如果为true,表示当前线程获取的锁,
                    // 为false,锁的状态被其他线程更改,当前线程获取的锁失败
                    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;
            }
    
            protected final boolean tryRelease(int releases) {
                // c表示新的锁的记录状态
                int c = getState() - releases;
                // 如果当前线程不是独占锁的线程,就抛出IllegalMonitorStateException异常
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                // 标志是否可以释放锁
                boolean free = false;
                // 当新的锁的记录状态为0时,表示可以释放锁
                if (c == 0) {
                    free = true;
                    // 设置独占锁的线程为null
                    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() {
                // 只有当前线程是独占锁的线程,才会返回锁的记录状态state,否则返回0
                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); // reset to unlocked state
            }
        }
    
        /**
         * 非公平锁
         */
        static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
    
            // 获取锁,如果没有获取到锁,则当前线程要阻塞等待
            final void lock() {
                // compareAndSetState返回true,表示当前线程获取锁成功。
                // 因为是非公平锁,所以不需要判断AbstractQueuedSynchronizer线程等待队列是否有值
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    // 调用acquire方法,获取锁
                    acquire(1);
            }
    
            // 尝试获取锁,获取到锁返回true,没有获取到返回false
            protected final boolean tryAcquire(int acquires) {
                // 调用父类的nonfairTryAcquire方法
                return nonfairTryAcquire(acquires);
            }
        }
    
        /**
         * 公平锁
         */
        static final class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
    
            final void lock() {
                // 与非公平锁不同,因为它要考虑线程等待队列是否有值
                acquire(1);
            }
    
            // 尝试获取锁,与与非公平锁最大的不同就是调用hasQueuedPredecessors()方法
            // hasQueuedPredecessors方法返回true,表示等待线程队列中有一个线程在当前线程之前,
            // 根据公平锁的规则,当前线程不能获取锁。
            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                // 获取锁的记录状态
                int c = getState();
                // 如果c==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 ReentrantLock() {
            sync = new NonfairSync();
        }
    
    
        // 根据fair值,决定创建公平锁还是非公平锁
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    
        // 获取锁,如果获取不到,就一直等待。不响应中断请求
        public void lock() {
            sync.lock();
        }
    
        // 获取锁,如果获取不到,就一直等待。如果有中断请求就抛出异常
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(1);
        }
    
        // 尝试用非公平锁方式去获取锁,立即返回。返回true表示获取成功,返回false表示获取失败
        public boolean tryLock() {
            return sync.nonfairTryAcquire(1);
        }
    
        // 在规定的unit时间内获取锁,如果时间到了还没有获取到锁,则返回false,表示获取失败
        // 如果在线程等待期间有中断请求就抛出异常
        public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(timeout));
        }
    
        // 当前线程释放占用的锁,并唤醒这个锁上的一个等待线程
        public void unlock() {
            sync.release(1);
        }
    
        // 创建一个Condition对象
        public Condition newCondition() {
            return sync.newCondition();
        }
    
        //
        public int getHoldCount() {
            return sync.getHoldCount();
        }
    
        // 当前线程是不是持有锁的线程
        public boolean isHeldByCurrentThread() {
            return sync.isHeldExclusively();
        }
    
        // 锁是否已经被持有
        public boolean isLocked() {
            return sync.isLocked();
        }
    
        // 是不是公平锁
        public final boolean isFair() {
            return sync instanceof FairSync;
        }
    
        // 返回持有锁的线程,如果null,表示没有任何线程持有锁
        protected Thread getOwner() {
            return sync.getOwner();
        }
    
        // 是不是有等待锁的线程,即锁的同步队列是不是不为空
        public final boolean hasQueuedThreads() {
            return sync.hasQueuedThreads();
        }
    
        // 等待锁的线程队列中有没有thread线程
        public final boolean hasQueuedThread(Thread thread) {
            return sync.isQueued(thread);
        }
    
        // 等待锁线程队列的长度
        public final int getQueueLength() {
            return sync.getQueueLength();
        }
    
        // 返回等到锁的线程队列的集合
        protected Collection<Thread> getQueuedThreads() {
            return sync.getQueuedThreads();
        }
    
        // condition对象的condition队列是否有等待线程
        public boolean hasWaiters(Condition condition) {
            if (condition == null)
                throw new NullPointerException();
            if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
                throw new IllegalArgumentException("not owner");
            return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
        }
    
        // condition对象上等待线程的个数
        public int getWaitQueueLength(Condition condition) {
            if (condition == null)
                throw new NullPointerException();
            if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
                throw new IllegalArgumentException("not owner");
            return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
        }
    
        // condition对象上等待线程的集合
        protected Collection<Thread> getWaitingThreads(Condition condition) {
            if (condition == null)
                throw new NullPointerException();
            if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
                throw new IllegalArgumentException("not owner");
            return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
        }
    
        public String toString() {
            Thread o = sync.getOwner();
            return super.toString() + ((o == null) ?
                                       "[Unlocked]" :
                                       "[Locked by thread " + o.getName() + "]");
        }
    }
    
    

    相关文章

      网友评论

        本文标题:JUC锁框架_ ReentrantLock原理分析

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