什么是可重入锁
可重入就是说某个线程已经获得某个锁,它可以再次获取这个锁而不会出现死锁。
可重入锁的作用
避免死锁。
例如:
用户名和密码都保存在txt文件中,然后有一个验证方法用于登录时验证,有一个更新密码的方法用于更改密码,这两个方法都需要加synchronized锁。当更新密码时,需要调用验证方法,此时就需要可重入锁。如果是不可重入锁,那么这个线程第二次获取这个锁时,会进行自旋(等待获取这个锁),造成死锁。
Java中的可重入锁
java中synchronized和ReentrantLock都是可重入锁,也都是独占锁。
synchronized锁
可重入:
package com.test.reen;
// 演示可重入锁是什么意思,可重入,就是可以重复获取相同的锁,synchronized和ReentrantLock都是可重入的
// 可重入降低了编程复杂性
public class WhatReentrant {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (this) {
System.out.println("第1次获取锁,这个锁是:" + this);
int index = 1;
while (true) {
synchronized (this) {
System.out.println("第" + (++index) + "次获取锁,这个锁是:" + this);
}
if (index == 10) {
break;
}
}
}
}
}).start();
}
}
原理:
每个对象都存在一个与之关联的monitor,加锁即线程持有了某个对象实例的monitor。
synchronized同步代码块的原理是在代码块的前面加monitorenter指令,获取对象的monitor,在代码块的后面加monitorexit指令,释放所持有的对象的monitor。
synchronized修饰的方法是用ACC_SYNCHRONIZED标识代替了上面的两个指令,该标识指明了这个方法是一个同步方法。
ReentrantLock锁
ReentrantLock 和 synchronized 不一样,需要手动释放锁,所以使用 ReentrantLock的时候一定要手动释放锁,并且加锁次数和释放次数要一样,否则会造成死锁
可重入:
package com.test.reen;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
// 演示可重入锁是什么意思
public class WhatReentrant2 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println("第1次获取锁,这个锁是:" + lock);
int index = 1;
while (true) {
try {
lock.lock();
System.out.println("第" + (++index) + "次获取锁,这个锁是:" + lock);
try {
Thread.sleep(new Random().nextInt(200));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (index == 10) {
break;
}
} finally {
lock.unlock();
}
}
} finally {
lock.unlock();
}
}
}).start();
}
}
原理:
ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
这个FIFO等待队列是AbstractQueuedSynchronizer简称为AQS队列,如果一个线程获取锁就直接成功,如果失败了就将其放入等待队列当中。
ReentrantLock默认实现非公平锁:
/**
* 默认创建非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
ReentrantLock和Synchronized比较
- 它们都是是独占锁。
- 它们都是可重入锁,但解锁方式有区别。synchronized加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
- synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断(它的tryLock()方法可以设置尝试拿锁的时间)。
参考资料:可重入锁详解(什么是可重入)_石头StoneWang的博客-CSDN博客_可重入