前言:

本文内容:可重入锁、自旋锁、死锁排查

推荐免费JUC并发编程视频:【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili

JUC笔记代码下载地址:

蓝奏云:下载地址 密码:joker

百度云:下载地址 提取码:3siw

可重入锁

公平锁和非公平锁

默认锁都是非公平锁

1
2
3
4
5
6
7
8
// 非公平锁(可以插队)
public ReentrantLock() {
sync = new NonfairSync();
}
// 公平锁 传一个布尔值
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

可重入锁

可重入锁(递归锁)

同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响。

synchronized可重入锁

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
package com.jokerdig.lock;

/**
* @author Joker大雄
* @data 2022/8/29 - 11:15
**/
// Synchronized
public class Demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}

}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"sms");
call(); // 这里也有锁
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+"call");
}
}

运行结果

1
2
3
4
5
6
Asms
Acall
Bsms
Bcall

Process finished with exit code 0

lock可重入锁

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
package com.jokerdig.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joker大雄
* @data 2022/8/29 - 11:21
**/
public class Demo02 {
public static void main(String[] args) {
Phone1 phone = new Phone1();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}

}
class Phone1{
Lock lock = new ReentrantLock();
public void sms(){
lock.lock(); // 第一把锁 上锁和解锁必须配锁,否则会产生死锁
try {
System.out.println(Thread.currentThread().getName()+"sms");
call(); // 这里也有锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call(){
lock.lock();// 第二把锁
try {
System.out.println(Thread.currentThread().getName()+"call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

运行结果

1
2
3
4
5
6
Asms
Acall
Bsms
Bcall

Process finished with exit code 0

自旋锁

当一个线程尝试去获取某一把锁的时候,如果这个锁此时已经被别人获取(占用),那么此线程就无法获取到这把锁,该线程将会等待,间隔一段时间后会再次尝试获取。这种采用循环加锁 -> 等待的机制被称为自旋锁(spinlock)

简单测试

SpinLockDemo

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
package com.jokerdig.lock;

import java.util.concurrent.atomic.AtomicReference;

/**
* @author Joker大雄
* @data 2022/8/29 - 11:33
**/
// 自旋锁
public class SpinLockDemo {
// int 0 Thread null
AtomicReference<Thread> atomicReference = new AtomicReference<>();

// 加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"==> myLock");
// 自旋锁
while (!atomicReference.compareAndSet(null,thread)){

}
}
// 解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"==> myUnLock");
atomicReference.compareAndSet(thread,null);
}
}

Test

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
package com.jokerdig.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joker大雄
* @data 2022/8/29 - 11:45
**/
public class Test {
public static void main(String[] args) throws InterruptedException {
// 自定义自旋锁
SpinLockDemo spinLockDemo = new SpinLockDemo();

new Thread(()->{
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinLockDemo.myUnLock();
}

},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinLockDemo.myUnLock();
}
},"B").start();
}
}

运行结果

1
2
3
4
5
6
A==> myLock
B==> myLock
A==> myUnLock
B==> myUnLock

Process finished with exit code 0

死锁排查

死锁概念

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

死锁

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
package com.jokerdig.lock;

import lombok.SneakyThrows;

import java.util.concurrent.TimeUnit;

/**
* @author Joker大雄
* @data 2022/8/29 - 11:54
**/
public class DeadLock {
public static void main(String[] args) {

String lockA = "lockA";
String lockB = "lockB";
// 锁A尝试获取锁B
new Thread(new myThread(lockA,lockB),"T1").start();
// 锁B尝试获取锁A
new Thread(new myThread(lockB,lockA),"T2").start();
}
}
class myThread implements Runnable{

private String lockA;
private String lockB;

public myThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}

@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread()+"lock:"+lockA+"get==>"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread()+"lock:"+lockB+"get==>"+lockA);
}
}

}
}

运行结果

程序一直在运行

1
2
Thread[T1,5,main]lock:lockAget==>lockB
Thread[T2,5,main]lock:lockBget==>lockA

死锁排查

  1. 使用jps -l定位进程号

    1
    2
    3
    4
    5
    6
    7
    D:\Project\project3\JUC>jps -l
    12272 sun.tools.jps.Jps
    4244 com.jokerdig.lock.DeadLock # 死锁的进程
    7896 org.jetbrains.jps.cmdline.Launcher
    8792 org.jetbrains.idea.maven.server.RemoteMavenServer36
    12652

  2. 使用jstack 进程号查看进程信息

    jstack 4244

    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
    Found one Java-level deadlock:  // 发现死锁
    =============================
    "T2":
    waiting to lock monitor 0x0000000002e9c098 (object 0x00000000d
    5effc70, a java.lang.String),
    which is held by "T1"
    "T1":
    waiting to lock monitor 0x0000000002e9ea88 (object 0x00000000d
    5effca8, a java.lang.String),
    which is held by "T2"

    Java stack information for the threads listed above:
    ===================================================
    "T2":
    at com.jokerdig.lock.myThread.run(DeadLock.java:42)
    - waiting to lock <0x00000000d5effc70> (a java.lang.Stri
    ng)
    - locked <0x00000000d5effca8> (a java.lang.String)
    at java.lang.Thread.run(Thread.java:748)
    "T1":
    at com.jokerdig.lock.myThread.run(DeadLock.java:42)
    - waiting to lock <0x00000000d5effca8> (a java.lang.Stri
    ng)
    - locked <0x00000000d5effc70> (a java.lang.String)
    at java.lang.Thread.run(Thread.java:748)

    Found 1 deadlock.