锁是否可重入

锁分为可重入锁和不可重入锁。 可重入和不可重入的概念是这样的:当一个线程获得了当前实例的锁,并进入方法A,这个线程在没有释放这把锁的时候,能否再次进入方法A呢?

  • 可重入锁

可以再次进入方法A,就是说在释放锁前此线程可以再次进入方法A(方法A递归)。

  • 不可重入锁(自旋锁)

不可以再次进入方法A,也就是说获得锁进入方法A是此线程在释放锁钱唯一的一次进入方法A。

可重入锁

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

可重入锁是一种特殊的互斥锁,它可以被同一个线程多次获取,而不会产生死锁。

在JAVA环境下 ReentrantLocksynchronized 都是 可重入锁

  1. 首先它是互斥锁:任意时刻,只有一个线程锁。

即假设A线程已经获取了锁,在A线程释放这个锁之前,B线程是无法获取到这个锁的,B要获取这个锁就会进入阻塞状态。

  1. 其次,它可以被同一个线程多次持有。

即,假设A线程已经获取了这个锁,如果A线程在释放锁之前又一次请求获取这个锁,那么是能够获取成功的。

举例说明:

Synchronized

  • SynchronizedDemo.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SynchronizedDemo implements Runnable { public synchronized void get(){ System.out.println(Thread.currentThread().getId()); set(); } public synchronized void set(){ System.out.println(Thread.currentThread().getId()); } @Override public void run() { get(); } public static void main(String[] args) { SynchronizedDemo ss=new SynchronizedDemo(); new Thread(ss).start(); new Thread(ss).start(); new Thread(ss).start(); } }
  • 运行日志
  [plaintext]
1
2
3
4
5
6
10 10 12 12 11 11

ReentrantLock

  • ReentrantLockDemo.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ReentrantLockDemo implements Runnable { ReentrantLock lock = new ReentrantLock(); public void get() { lock.lock(); System.out.println(Thread.currentThread().getId()); set(); lock.unlock(); } public void set() { lock.lock(); System.out.println(Thread.currentThread().getId()); lock.unlock(); } @Override public void run() { get(); } public static void main(String[] args) { ReentrantLockDemo ss = new ReentrantLockDemo(); new Thread(ss).start(); new Thread(ss).start(); new Thread(ss).start(); } }
  • 输出日志
  [plaintext]
1
2
3
4
5
6
10 10 11 11 12 12

不可重入锁

自旋锁

  • SpinLock.java

对于自旋锁来说,

1、若有同一线程两调用lock() ,会导致第二次调用lock位置进行自旋,产生了死锁 说明这个锁并不是可重入的。(在lock函数内,应验证线程是否为已经获得锁的线程)

2、若1问题已经解决,当unlock()第一次调用时,就已经将锁释放了。实际上不应释放锁。 (采用计数次进行统计)

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class SpinLock implements Runnable { private AtomicReference<Thread> owner =new AtomicReference<>(); public void lock(){ Thread current = Thread.currentThread(); while(!owner.compareAndSet(null, current)){ } } public void unlock (){ Thread current = Thread.currentThread(); owner.compareAndSet(current, null); } public void get() { this.lock(); System.out.println(Thread.currentThread().getId()); set(); this.unlock(); } public void set() { this.lock(); System.out.println(Thread.currentThread().getId()); this.unlock(); } @Override public void run() { get(); } public static void main(String[] args) { SpinLock lock = new SpinLock(); new Thread(lock).start(); new Thread(lock).start(); new Thread(lock).start(); } }
  • 输出日志
  [plaintext]
1
2
3
10 // 卡死

自旋锁改进

改进成为可重入锁

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class SpinLockBetter implements Runnable { private AtomicReference<Thread> owner = new AtomicReference<>(); private int count = 0; public void lock() { Thread current = Thread.currentThread(); // 判断是否已经拥有此线程 if (current == owner.get()) { count++; return; } while (!owner.compareAndSet(null, current)) { } } public void unlock() { Thread current = Thread.currentThread(); if (current == owner.get()) { if (count != 0) { count--; } else { owner.compareAndSet(current, null); } } } public void get() { this.lock(); System.out.println(Thread.currentThread().getId()); set(); this.unlock(); } public void set() { this.lock(); System.out.println(Thread.currentThread().getId()); this.unlock(); } @Override public void run() { get(); } public static void main(String[] args) { SpinLockBetter ss = new SpinLockBetter(); new Thread(ss).start(); new Thread(ss).start(); new Thread(ss).start(); } }

改进后,可以和前面一样正常运行。

参考资料

https://blog.csdn.net/soonfly/article/details/70918802

http://blog.jobbole.com/108571/

https://www.cnblogs.com/cposture/p/SpinLock.html

https://blog.csdn.net/yanyan19880509/article/details/52345422