当前位置:网站首页>ReentrantLock的使用和原理详解
ReentrantLock的使用和原理详解
2022-08-02 03:27:00 【浮空over】
一、ReentrantLock 小例子
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest extends Thread {
public static ReentrantLock lock = new ReentrantLock();
public static int i = 0;
public ReentrantLockTest(String name) {
super.setName(name);
}
@Override
public void run() {
for (int j = 0; j < 10000000; j++) {
lock.lock();
try {
System.out.println(this.getName() + " " + i);
i++;
} finally {
lock.unlock();
}
}
}
/** * @param args * @throws InterruptedException */
public static void main(String[] args) throws InterruptedException {
ReentrantLockTest test1 = new ReentrantLockTest("thread1");
ReentrantLockTest test2 = new ReentrantLockTest("thread2");
test1.start();
test2.start();
test1.join();
test2.join();
System.out.println(i);
}
}
最后的结果是
20000000;如果去掉锁,那么输出结果是一个小于20000000的不确定的数
二、ReentrantLock的优点
- java中已经有了内置锁:
synchronized,synchronized的特点是使用简单,一切交给JVM去处理,不需要显示释放 - 从用法上可以看出,与
synchronized相比,ReentrantLock就稍微复杂一点。因为必须在finally中进行解锁操作,如果不在 finally解锁,有可能代码出现异常锁没被释放,
那么为什么要引入ReentrantLock呢?
在jdk1.5里面,
ReentrantLock的性能是明显优于synchronized的,但是在jdk1.6里面,synchronized做了优化,他们之间的性能差别已经不明显了。ReentrantLock并不是一种替代内置加锁的方法,而是作为一种可选择的高级功能。相比于
synchronized,ReentrantLock在功能上更加丰富,它具有可重入、可中断、可限时、公平锁等特点。
1.可重入(其实synchronized 也是可重入的)
避免堵塞:lock1.lock()获取lock1对象的锁,while1中的lock1.lock()也是尝试获取lock1对象的锁,但是lock1对象的锁已经在上面被获取了,所以这个程序会一直阻塞在这里,但是有了可重入锁,这里不再需要获取这个锁了,直接往下运行。
提高效率:拿一把锁,只要没有unlock释放,可以重复使用,不需要一直获取同一把锁
lock1.lock();
while(...){
//上面已拿到锁,这里可以重复使用
lock1.lock();
//lock2还要重新获取锁
lock2.lock();
}
由于
ReentrantLock是重入锁,所以可以反复得到相同的一把锁,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放(重入锁)。
lock的底层是用CAS实现的,并且expect是0,update是1,当期望值是0相等的时候,把它替换成1,这样相当于拿到了锁,但是当真实值不等于期望值,就说明这个锁已经被别的线程拿了,需要等待释放。只有一个人能够将状态成功的更新为1,更新之后,和期望值不相等,其余线程都无法更新。
2.可中断
与synchronized不同的是,ReentrantLock对中断是有响应的.synchronized一旦尝试获取锁就会一直等待直到获取到锁。
构造一个死锁的例子,然后用中断来处理死锁
代码如下:
package concurrency.in.practice;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.locks.ReentrantLock;
public class LockInterrupt extends Thread {
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lock;
public LockInterrupt(int lock, String name) {
super(name);
this.lock = lock;
}
@Override
public void run() {
try {
if (lock == 1) {
lock1.lockInterruptibly();
try {
Thread.sleep(500);
} catch (Exception e) {
// TODO: handle exception
}
lock2.lockInterruptibly();
} else {
lock2.lockInterruptibly();
try {
Thread.sleep(500);
} catch (Exception e) {
// TODO: handle exception
}
lock1.lockInterruptibly();
}
} catch (Exception e) {
// TODO: handle exception
} finally {
if (lock1.isHeldByCurrentThread()) {
lock1.unlock();
}
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}
System.out.println(Thread.currentThread().getId() + ":线程退出");
}
}
public static void main(String[] args) throws InterruptedException {
LockInterrupt t1 = new LockInterrupt(1, "LockInterrupt1");
LockInterrupt t2 = new LockInterrupt(2, "LockInterrupt2");
t1.start();
t2.start();
Thread.sleep(1000);
//DeadlockChecker.check();
}
static class DeadlockChecker {
private final static ThreadMXBean mbean = ManagementFactory
.getThreadMXBean();
public static void check() {
Thread tt = new Thread(() -> {
{
// TODO Auto-generated method stub
while (true) {
long[] deadlockedThreadIds = mbean.findDeadlockedThreads();
if (deadlockedThreadIds != null) {
ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds);
for (Thread t : Thread.getAllStackTraces().keySet()) {
for (int i = 0; i < threadInfos.length; i++) {
if (t.getId() == threadInfos[i].getThreadId()) {
System.out.println(t.getName());
t.interrupt();
}
}
}
}
try {
Thread.sleep(5000);
} catch (Exception e) {
// TODO: handle exception
}
}
}
});
tt.setDaemon(true);
tt.start();
}
}
}
执行后,出现了死锁,可通过中断来停止线程
3.可限时
- 超时不能获得锁,就返回false,不会永久等待构成死锁
- 使用
lock.tryLock(long timeout, TimeUnit unit)来实现可限时锁,参数为时间和单位。
例子:
package concurrency.in.practice;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockTest extends Thread {
public static ReentrantLock lock = new ReentrantLock();
public TryLockTest(String name){
super(name);
}
@Override
public void run() {
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
Thread.sleep(6000);
} else {
System.out.println(this.getName() + " get lock failed");
}
} catch (Exception e) {
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println("lock.isHeldByCurrentThread: " + this.getName());
lock.unlock();
}
}
}
public static void main(String[] args) {
TryLockTest t1 = new TryLockTest("TryLockTest1");
TryLockTest t2 = new TryLockTest("TryLockTest2");
t1.start();
t2.start();
}
}
两个线程来争夺一把锁,获得锁的线程sleep6秒,每个线程都只尝试5秒去获得锁。
所以必定有一个线程无法获得锁。无法获得后就直接退出了。
3.公平锁
- 一般意义上的锁是不公平的,不一定先来的线程能先得到锁,后来的线程就后得到锁。不公平的锁可能会产生饥饿现象。
- 公平锁的意思就是,这个锁能保证线程是先来的先得到锁。虽然公平锁不会产生饥饿现象,但是公平锁的性能会比非公平锁差很多。
- 默认是非公平锁,可修改参数new ReentrantLock(true)实现公平锁
使用方法:
public ReentrantLock(boolean fair)
public static ReentrantLock fairLock = new ReentrantLock(true);
边栏推荐
- (1) the print () function, escape character, binary and character encoding, variables, data type, the input () function, operator
- CSRF(跨站请求伪造)
- CTF introductory notes ping
- 库存现金、现金管理制度、现金的账务处理、银行存款、银行存款的账务处理、银行存款的核对
- Go Build报错汇总(持续更新)
- 公司产品太多了,怎么实现一次登录产品互通?
- php的curl函数模拟post数据提交,速度非常慢
- CSRF (Cross Site Request Forgery)
- 在 UUP dump 被墙的情况下如何用 UUP 下载 ISO 镜像
- ontop-vkg 学习1
猜你喜欢

重点考:从债劵的角度来看交易性金融资产

(1) the print () function, escape character, binary and character encoding, variables, data type, the input () function, operator

Laravel打印执行的SQL语句

(2) Sequence structures, Boolean values of objects, selection structures, loop structures, lists, dictionaries, tuples, sets

文件包含漏洞

Kotlin - 标准函数(with、run和apply)

laravel-admin FROM表单同行展示问题

什么是广告电商商业模式?这几个门派告诉你

关于我的项目-微信公众号~

元宇宙:为何互联网大佬纷纷涉足?元宇宙跟NFT是什么关系?
随机推荐
如何在正则表达式里表达可能存在也可能不存在的内容?
mysql 原生语句点滴学习记录
SGDP(2)——声纳寻宝游戏
备战金九银十:Android 高级架构师的学习路线及面试题分享
(3) string
会计账簿、会计账簿概述、会计账簿的启用与登记要求、会计账簿的格式和登记方法
【一句话攻略】彻底理解JS中的回调(Callback)函数
如何一步一步的:玩转全民拼购!
ontop-vkg 学习1
C language uses stack to calculate infix expressions
web安全之目录遍历
centos8 安装搭建php环境
还原最真实、最全面的一线大厂面试题
Kotlin - 静态方法
面试知识点整理:Skia 架构的场景渲染
hackmyvm: kitty walkthrough
同时安装VirtualBox和VMware,虚拟机如何上网
(2) Sequence structures, Boolean values of objects, selection structures, loop structures, lists, dictionaries, tuples, sets
laravel 写api接口时 session获取不到处理办法
文件包含漏洞