1、什么是可重入锁?它有什么作用?
可重入锁,也叫做递归锁,指的是在同一线程内,外层函数获得锁之后,内层递归函数仍然可以获取到该锁。 说白了就是同一个线程再次进入同样代码时,可以再次拿到该锁。 它的作用是:防止在同一线程中多次获取锁而导致死锁发生。

2、那么java中谁实现了可重入锁了?
在java的编程中synchronized 和 ReentrantLock都是可重入锁。我们可以参考ReentrantLock的代码

3、基于ReentrantLock的可重入锁
ReentrantLock,是一个可重入且独占式的锁,是一种递归无阻塞的同步锁。


对于不同线程则相当于普通的互斥锁。

在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁

 

java线程是基于“每线程(per-thread)”,而不是基于“每调用的(per-invocation)”的,也就是说java为每个线程分配一个锁,而不是为每次调用分配一个锁。

 

最大的作用是避免死锁。
在很多情况下线程需要多次进入锁内执行任务。
我讲一个应用场景就是比如数据库事务的实现过程中。

场景:
add操作将会获取锁,若一个事务当中多次add,就应该允许该线程多次进入该临界区。
synchronized锁也是个可重入锁,
比如一个类当中的两个非静态方法都被synchronized修饰,则线程在获取synchronized锁访问一个方法时是可以进入另一个synchronized方法的
(PS:应该也不能进入static方法的synchronized修饰临界区的,因为是两把不同的锁,表现的不是可重入的特性)

比如我今天遇到的一个场景:用户名和密码保存在本地txt文件中,则登录验证方法和更新密码方法都应该被加synchronized,那么当更新密码的时候需要验证密码的合法性,所以需要调用验证方法,此时是可以调用的。



作者:郭无心


 

 

 

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及。本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑。

 

四、可重入锁:

本文里面讲的是广义上的可重入锁,而不是单指JAVA下的ReentrantLock。

可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁

下面是使用实例

01  public class Test implements Runnable{
02
03 public synchronized void get(){
04 System.out.println(Thread.currentThread().getId());
05 set();
06 }
07
08 public synchronized void set(){
09 System.out.println(Thread.currentThread().getId());
10 }
11
12 @Override
13 public void run() {
14 get();
15 }
16 public static void main(String[] args) {
17 Test ss=new Test();
18 new Thread(ss).start();
19 new Thread(ss).start();
20 new Thread(ss).start();
21 }
22 }
01 public class Test implements Runnable {
02 ReentrantLock lock = new ReentrantLock();
03
04 public void get() {
05 lock.lock();
06 System.out.println(Thread.currentThread().getId());
07 set();
08 lock.unlock();
09 }
10
11 public void set() {
12 lock.lock();
13 System.out.println(Thread.currentThread().getId());
14 lock.unlock();
15 }
16
17 @Override
18 public void run() {
19 get();
20 }
21
22 public static void main(String[] args) {
23 Test ss = new Test();
24 new Thread(ss).start();
25 new Thread(ss).start();
26 new Thread(ss).start();
27 }

两个例子最后的结果都是正确的,即 同一个线程id被连续输出两次。

结果如下:

Threadid: 8
Threadid: 8
Threadid: 10
Threadid: 10
Threadid: 9
Threadid: 9

可重入锁最大的作用是避免死锁
我们以自旋锁作为例子,

  public class SpinLock {
02 private AtomicReference<Thread> owner =new AtomicReference<>();
03 public void lock(){
04 Thread current = Thread.currentThread();
05 while(!owner.compareAndSet(null, current)){
06 }
07 }
08 public void unlock (){
09 Thread current = Thread.currentThread();
10 owner.compareAndSet(current, null);
11 }

对于自旋锁来说,
1、若有同一线程两调用lock() ,会导致第二次调用lock位置进行自旋,产生了死锁
说明这个锁并不是可重入的。(在lock函数内,应验证线程是否为已经获得锁的线程)
2、若1问题已经解决,当unlock()第一次调用时,就已经将锁释放了。实际上不应释放锁。
(采用计数次进行统计)
修改之后,如下:

  public class SpinLock1 {
02 private AtomicReference<Thread> owner =new AtomicReference<>();
03 private int count =0;
04 public void lock(){
05 Thread current = Thread.currentThread();
06 if(current==owner.get()) {
07 count++;
08 return ;
09 }
10
11 while(!owner.compareAndSet(null, current)){
12
13 }
14 }
15 public void unlock (){
16 Thread current = Thread.currentThread();
17 if(current==owner.get()){
18 if(count!=0){
19 count--;
20 }else{
21 owner.compareAndSet(current, null);
22 }
23
24 }​​
​​}​​

该自旋锁即为可重入锁。