您现在的位置是:首页 > 编程 > 

【连载 08】lock锁

2025-07-27 13:10:09
【连载 08】lock锁 2. lock锁如果你曾经遭遇过线程不安全的问题,一定不会对“锁”这个概念不陌生。实际上绝大多数线程安全的先解决方案都离不开“锁”。JDK里面就有一个接口java.util.concurrent.locks.Lock,顾名思义,就是并发包中“锁”,大量的线程安全问题解决方案均是依赖这个接口的实现类。就跟synchronized关键字一样,在性能测试实战中只要掌握基本的功

【连载 08】lock锁

2. lock锁

如果你曾经遭遇过线程不安全的问题,一定不会对“锁”这个概念不陌生。实际上绝大多数线程安全的先解决方案都离不开“锁”。

JDK里面就有一个接口java.locks.Lock,顾名思义,就是并发包中“锁”,大量的线程安全问题解决方案均是依赖这个接口的实现类。就跟synchronized关键字一样,在性能测试实战中只要掌握基本的功能和最佳实战即可,这里再重复一下上一节的建议:如需使用 Lock实现的功能过于复杂,建议抛开Lock,寻更加简单、可靠,已验证的解决方案。

在性能测试中最常用的java.locks.Lock实现类就是可重入锁:java.locks.ReentrantLock。相比synchronizedReentrantLock拥有以下主要优点:

可重入性。ReentrantLock允许已经获取锁的线程再次获取锁,相比d更加安全,避免发生死锁的情况。

更加灵活。d提供多个API完成锁的获取和释放,让使用者拥有更多选择。

可中断性。ReentrantLock功能中,获取锁的线程可以被主动中断,相比synchronized无限等待,更加适合处理锁的超时场景。

更高的性能。除了提供多种获取锁的API以外,ReentrantLock还提供两种锁类型:公平锁和非公平锁,帮助程序提升在加锁场景的性能。

ReentrantLock提供了中获取锁的API,分别是:阻塞锁、可中断锁和超时锁。下面分别用代码演示如何使用。

2..1 阻塞锁

获取阻塞锁的方法是:java.locks.ReentrantLock#lock,没有参数。该方法会尝试获取锁。当无法获取锁时,当前线程会处于休眠状态,直到获取锁成功。

演示代码如下:

代码语言:javascript代码运行次数:0运行复制

package org.funtester.performance.section;

import java.locks.ReentrantLock;

/**

 * 阻塞锁示例

 */

public class BlockingLockDemo {

    public static void main(String[] args) throws InterruptedException {

        ReentrantLock lock = new ReentrantLock();// 创建一个可重入锁

        Thread lockTestThread = new Thread(() -> {// 创建一个线程

            println(() + "  异步线程启动!  " + ().getame());// 打印日志

            lock.lock();// 获取锁

            println(() + "  获取到锁了!  " + ().getame());// 打印日志

            lock.unlock();// 释放锁

        });

        lock.lock();// 获取锁

        lockTestThread.start();// 启动异步线程

        Thread.sleep(100);// 睡眠100毫秒

        println(() + "  即将释放锁!  " + ().getame());// 打印日志

        lock.unlock();// 释放锁

    }

}

这个例子中,首先创建了一个异步线程,执行代码逻辑为:获取锁,打印日志,释放锁。然后在main线程中,先获取锁,再启动异步线程。然后main线程休眠100毫秒,再释放锁。控制台输出内容如下:

代码语言:javascript代码运行次数:0运行复制
16984775568  异步线程启动!  Thread-0

169847755471  即将释放锁!  main

169847755471  获取到锁了!  Thread-0

可以看到,异步线程在启动之后,等待了100毫秒才获取到锁,并打印日志,且这个操作也是在main线程释放锁之后进行的。原因是因为main线程先于异步线程获取到锁了,所以在main线程释放锁之前,异步线程只能无所事事,干等着。

阻塞锁和synchronized解决线程安全思路和使用方法上比较相似,而且在性能测试工作中使用场景大多重合。阻塞锁在一定程度上可以替代synchronized,特别是在编写流程式的代码中。

2..2 可中断锁

可中断锁的获取方法是:java.locks.ReentrantLock#lockInterruptibly,没有参数。该方式会尝试获取锁,并且是阻塞的,但当未获取到锁时,如果当前线程被设置了中断状态,则会抛出 java.lang.InterruptedException 异常。

下面是演示代码:

代码语言:javascript代码运行次数:0运行复制
package org.funtester.performance.section;

import java.locks.ReentrantLock;

/**

 * 可中断锁示例

 */

public class InterruptiblyLockDemo {

    public static void main(String[] args) throws InterruptedException {

        ReentrantLock lock = new ReentrantLock();// 创建一个可重入锁

        Thread lockTestThread = new Thread(() -> {// 创建一个线程

            try {

                lock.lockInterruptibly();// 获取锁

                println(() + "  获取到锁了!  " + ().getame());// 打印日志

                lock.unlock();// 释放锁

            } catch (InterruptedException e) {

                println(() + "  线程被中断了!  " + ().getame());// 打印日志

            }

        });

        lock.lock();// 获取锁

        lockTestThread.start();// 启动异步线程

        lockTestThread.interrupt();// 中断异步线程

        lock.unlock();// 释放锁

    }

}

在这个例子中,首先创建了一个异步线程,执行代码逻辑为获取锁(可中断),打印日志,释放锁。然后让main线程先获取锁,然后启动异步线程,再中断异步线程。下面来就控制台输出:

1698478061924 线程被中断了! Thread-0

这里看到只有一行输出,即异步线程再获取锁时被中断了,抛出的异常被捕获。

可中断锁继承了阻塞锁的有点,提供了将线程从等待中解脱的方案,在使用上更加广泛。可中断锁可以进行线程间超时控制、防止无限等待,可以非常优雅地关闭被阻塞的线程,释放资源。可中断锁适合多线程协作的场景,要求使用者对多线程了解也更高。

2.. 超时锁

超时锁的获取方法有两个:java.locks.ReentrantLock#tryLock()java.locks.ReentrantLock#tryLock(long, java.TimeUnit),返回Boolean值,表示获取锁是否成功。第二个API参数设置超时时间。这两个API前者可以简单理解为后者时间设置为0,含义是尝试获取一次,返回结果。

演示代码如下:

代码语言:javascript代码运行次数:0运行复制

package org.funtester.performance.section;

import java.TimeUnit;

import java.locks.ReentrantLock;

/**

 * 超时锁示例

 */

public class TimeoutLockDemo {

    public static void main(String[] args) throws InterruptedException {

        ReentrantLock lock = new ReentrantLock();// 创建一个可重入锁

        Thread lockTestThread = new Thread(() -> {// 创建一个线程

            boolean b = ();// 第一次尝试获取锁

            println(() + "  第一次获取锁的结果:" + b + "  " + ().getame());// 打印日志

            try {

                boolean b1 = (, TimeUnit.SECODS);

                println(() + "  第二次获取锁的结果:" + b1 + "  " + ().getame());

            } catch (InterruptedException e) {

                println(() + "  第二次获取锁中断了  " + ().getame());

            }

        });

        lock.lock();// 获取锁

        lockTestThread.start();// 启动异步线程

        Thread.sleep(100);// 睡眠100毫秒

        lock.unlock();// 释放锁

    }

}

在这个例子中,依旧先创建一个异步线程,执行的逻辑为:首先尝试获取一次并且打印结果,然后第二次尝试获取,设置超时时间秒,并打印结果。main线程依旧先获取锁,然后启动异步线程,休眠100 ms然后释放锁。例子中,为了简化代码,笔者并没有编写依据获取锁的结果释放锁的代码。控制台输出内容如下:

代码语言:javascript代码运行次数:0运行复制
169847940990  第一次获取锁的结果:false  Thread-0

169847941090  第二次获取锁的结果:true  Thread-0

可以看到第一次获取锁失败了,原因是该锁正在被main线程持有。第二获取锁成功了,因为main线程持有锁100毫秒之后便释放锁。在异步线程第二次获取锁的秒超时时间内,它成功了,所以获取到了锁。

在三种锁的方法中,超时锁在性能测试中使用最广泛。它提供了一种简单、可靠的控制锁等待时间的方式。相比可中断锁,超时锁对新手更加容易上手,无须掌握线程间统信、调度的知识。

2..4 公平锁和非公平锁

代码语言:javascript代码运行次数:0运行复制
java.locks.ReentrantLock 有一个构造方法,如下:

/** 

 * Creates an instance of {@code ReentrantLock} with the 

 * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy 

 */public ReentrantLock(boolean fair) { 

    sync = fair ? new FairSync() : new onfairSync(); 

}

方法参数中Boolean值,含义即是否使用公平锁。无参的构造方法默认使用的非公平锁。公平锁和非公平锁的主要区别是获取锁的方式不同。公平锁的获取是公平的,线程依次排队获取锁。谁等待的时间最长,就由谁获得锁。非公平锁获取是随机的,谁先请求谁先获得锁,不一定按照请求锁的顺序来。ReentrantLock默认的是非公平锁,相比公平锁拥有更高性能。

2..5 最佳实战

在性能测试实战中, java.locks.ReentrantLock而言 ,常用最佳实战非常容易掌握。那就是使用 try-catch-finally 语法实现,演示案例如下:

代码语言:javascript代码运行次数:0运行复制
boolean status = false; 

try { 

    status = (, TimeUnit.SECODS); 

} catch (Exception e) { 

    // 异常处理 

} finally { 

    if (status) lock.unlock(); 

}

在使用ReentrantLock解决线程安全问题时,有几点注意事项:

  • 必须主动进行锁管理。与synchronized不同,ReentrantLock要求必需显示获取和释放锁,特别在释放锁时,最简单的方法就是按照最佳实战,将其放在finally中执行。
  • 竭力避免死锁。不要混合使用不同锁;不要在一个功能中使用过多的锁和synchronized关键字;避免多次获取锁;使用使用 lockInterruptibly() 获取锁,如果在等待锁的过程中线程被中断,需要有处理代码进行后续处理。
  • 尽量使用ReentrantLock默认的非公平锁。

虽然java.locks.ReentrantLock叫可重入锁,但是在性能测试实践当中,不建议使用可重入功能。主要原因以下两点:

  • (1)增加锁竞争,影响性能。使用不当会导致同一个线程频繁获取和释放锁,增加竞争,降低程序性能。
  • (2)死锁风险。如果同步代码中多次使用锁,就需要严格释放锁流程,一旦发生异常而没有捕获处理,则会造成死锁风险。

在笔者的性能测试生涯中,没有必须使用可重入特性的场景,所以在性能测试实践中,应当尽量避免使用该特性,防止异常情况发生。

书的名字:从 Java 开始做性能测试

如果本书内容对你有所帮助,希望各位不吝赞赏,让我可以贴补家用。赞赏两位数可以提前阅读未公开章节。我也会尝试制作本书的视频教程,包括必要的答疑。

本文参与 腾讯云自媒体同步曝光计划,分享自。原始发表:2024-12-0,如有侵权请联系 cloudcommunity@tencent 删除日志线程异步性能测试lock

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/biancheng/1132654.html

相关标签:无
上传时间: 2025-07-18 21:02:10
留言与评论(共有 16 条评论)
本站网友 谷歌日语输入法
11分钟前 发表
笔者并没有编写依据获取锁的结果释放锁的代码
本站网友 打秋风是什么意思
22分钟前 发表
先获取锁
本站网友 浙江亲子鉴定
28分钟前 发表
获取锁的线程可以被主动中断
本站网友 中国美林湖
20分钟前 发表
再启动异步线程
本站网友 长沙房价走势
29分钟前 发表
提供了将线程从等待中解脱的方案
本站网友 服务器操作系统下载
11分钟前 发表
特别是在编写流程式的代码中
本站网友 甘肃武威新闻
15分钟前 发表
执行代码逻辑为:获取锁
本站网友 上海宜家家居地址
0秒前 发表
不建议使用可重入功能
本站网友 去除眼部细纹的方法
25分钟前 发表
它提供了一种简单
本站网友 北京上地二手房
25分钟前 发表
表示获取锁是否成功
本站网友 日喀则租房
8分钟前 发表
防止无限等待
本站网友 国防科技大学怎么样
26分钟前 发表
就跟synchronized关键字一样
本站网友 esd转iso
8分钟前 发表
这里再重复一下上一节的建议:如需使用 Lock实现的功能过于复杂
本站网友 i57
25分钟前 发表
并打印结果
本站网友 瀚斯宝丽
26分钟前 发表
虽然java.locks.ReentrantLock叫可重入锁