Java线程安全ReentrantLock

在学习Java语言的过程中,多线程是一个算是进阶的选择。我最近又学到了一个新技能ReentrantLock类,这个应该目前最简单的线程安全使用方式了,当然暴力synchronized除外。

首先回顾一下之前的线程安全常用同步类的文章:

下面介绍一下ReentrantLock类,看名字就是一个可重入锁,这个概念跟synchronized中有点类似。我理解为这是一个简单易上手的线程安全操作类,不需要理解synchronized带来的语法,不需要其他线程同步类锁需要的对各类API功能记忆。ReentrantLock真的是一个非常好用的多线程安全工具类。

ReentrantLock核心(个人看法)有两个方法lock()unlock(),顾名思义,一个是加锁一个是释放锁,所有线程安全的操作可以写在这两个方法之间。这一点跟之前的文章如何mock固定QPS的接口moco固定QPS接口升级补偿机制中用到的线程安全之流量控制类java.util.concurrent.Semaphore的使用基本一致,相比之下java.util.concurrent.locks.ReentrantLock相当于new java.util.concurrent.Semaphore((1, true)java.util.concurrent这个包简直就是一个宝藏,欢迎有兴趣学习的童鞋翻一翻源码,其乐无穷。

下面演示一下ReentrantLock的基本使用,中间用到了- Java自定义异步功能实践利用守护线程隐式关闭线程池中用到的异步关键字,有兴趣的可以翻一翻,就是关键字fun后面的代码块会有单独线程执行,这里用Java语言进行演示。

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        fun(() -> {
            lock.lock();
            output("FunTester获取锁了");
            sleep(2.0);
            lock.unlock();
            output("FunTester释放锁了");
            return null;
        });
        sleep(1.0);
        lock.lock();
        output("main线程获取锁");
        lock.unlock();
        output("main线程释放锁");
    }

控制台输出如下:

INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main 守护线程:FT-D开启!
INFO-> FT-1   FunTester获取锁了
INFO-> FT-1   FunTester释放锁了
INFO-> main main线程获取锁
INFO-> main main线程释放锁
WARN-> FT-D 异步线程池关闭!

Process finished with exit code 0

我觉得这两个API使用基本满足了常见的多线程编程需求了,唯一需要注意的就是unlock()方法可能因为程序异常不会被执行,所以一般都放在finally代码块中。

下面介绍几个非常用的API,有助于解决非常见的需求:

获取锁,但是有超时时间,会返回是否加锁成功。

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

获取等待队列长度,这个不准,因为队列可能会发生变化:

    public final int getQueueLength() {
        return sync.getQueueLength();
    }

判断是否上锁,这个和上面tryLocal功能上有点重复:

    public boolean isLocked() {
        return sync.isLocked();
    }

还有一个标准较不常用的,获取当前线程持有锁的次数,这个牵扯到ReentrantLock重入锁机制,就是一个线程可以持有很多次,但是只要次数不为零,其他线程都无法加锁成功,平时使用不多,没有谁单线程持有N多次锁:

    public int getHoldCount() {
        return sync.getHoldCount();
    }

Have Fun ~ Tester !