ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java多线程学习之ReentrantLock

2022-05-10 14:34:07  阅读:191  来源: 互联网

标签:Java lock ReentrantLock 获取 线程 return 多线程 final


一、什么是ReentrantLock

ReentrantLock中文译为‘可重入锁’,是‘java.util.concurrent.locks’包下的一个类,实现了‘Lock’接口,在多线程中用来保证线程安全。
使用demo:
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    int i = 1;
                    lock.lock();
                    System.out.println("第" + i + "次获取锁,这个锁是:" + lock);
                    for (i = 2; i < 10; i++) {
                        try {
                            lock.lock();
                            System.out.println("第" + i + "次获取锁,这个锁是:" + lock);
                        } finally {
                            lock.unlock();

                        }
                    }
                } finally {
                    lock.unlock();

                }
            }
        }).start();
    }

二、公平锁和非公平锁

ReentrantLock有三个内部类:Sync、NonfairSync、FairSync。Sync继承了抽象类AbstractQueuedSynchronizer(俗称AQS),NonfairSync、FairSync都继承了Sync,分别采用了非公平锁和公平锁的策略去获取锁。

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。
优点:所有的线程都能得到资源,不会饿死在队列中。
缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

非公平锁:多个线程去获取锁的时候,会直接先去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。
优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
缺点:这样可能导致队列中间的某个线程一直获取不到锁或者长时间获取不到锁。

根据ReentrantLock的构造函数可知:ReentrantLock默认采用非公平锁

    public ReentrantLock() {
        sync = new NonfairSync();
    }

三、ReentrantLock和AQS

1:ReentrantLock和AQS的关系

ReentrantLock使用demo:

    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();
        try {
            reentrantLock.lock();
            System.out.println("lock");
        } finally {
            reentrantLock.unlock();
        }
    }

点进lock()和unlock()方法,发现实际上是内部类Sync在调用lock()和unlock()方法,而Sync又是继承的AbstractQueuedSynchronizer(AQS),所以ReentrantLock执行的方法实际上是通过AQS来实现的!

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

 2:Sync、NonfairSync、FairSync源码分析

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();
            //获取AQS的state值
            int c = getState();
            if (c == 0) {
                //如果state为0说明资源未被占用即当前线程获取资源成功
                //用CAS更新state
                if (compareAndSetState(0, acquires)) {
                    //设置占用线程为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
                //如果获取失败则把线程改为占用资源的线程
            } else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // 正常情况下state不可能小于0
                    throw new Error("Maximum lock count exceeded");
                //更新state值
                setState(nextc);
                return true;
            }
            return false;
        }

        //释放资源
        protected final boolean tryRelease(int releases) {
            //获取当前state
            int c = getState() - releases;
            //释放资源的前提是占据了资源,否则不正常,报错!
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //state为0说明当前线程是最后一个获取资源的线程,释放后设置占用资源线程为空
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            //更新state
            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();
        }

        //获取state值
        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); // 重置未被锁的状态
        }
    }

NonfairSync:

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

        //尝试获取资源,实际是Sync中的“非公平获取”方法的结果
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

FairSync:

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

        //上锁
        final void lock() {
            //直接加入到队列中,等待获取资源
            acquire(1);
        }

        //尝试获取资源
        protected final boolean tryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            //获取state
            int c = getState();
            if (c == 0) {
                //等待队列中前面没有其他线程且能成功修改state值时才把自己设为占据资源线程
                if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
                    //设为资源拥有者
                    setExclusiveOwnerThread(current);
                    return true;
                }
                //如果当前线程已占据资源的话,不舍弃资源,仅修改state值
            } else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            //如果当前线程没有占据资源,且当前state值不为0,则返回false
            return false;
        }
    }

 分析:

ReentrantLock的“lock”:实际上是内部‘NonfairSync’或‘FairSync’调用的lock()方法,而两者的区别在于:NonfairSync(非公平锁)会先判断能否获取资源,获取不到再把线程加到等待队列中;FairSync(公平锁)会直接加到等待队列中,等轮到自己的时候才能获取到资源。

ReentrantLock的“unlock”:实际是内部类Sync的release()方法,而在‘release()’中又调用了‘tryRelease(arg)’,因为‘tryRelease’方法是在内部类‘Sync’中实现的,所以并不分“公平锁”和“非公平锁”,即ReentrantLock无论用公平锁还是非公平锁,它的解锁方法都是统一的。

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

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

文章参考:https://blog.csdn.net/zhengzhaoyang122/article/details/110847701

标签:Java,lock,ReentrantLock,获取,线程,return,多线程,final
来源: https://www.cnblogs.com/Bernard94/p/16249188.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有