多线程-Part06 | Eloise's Paradise
0%

多线程-Part06

本章节主要介绍ReentrantLock的可重入,可打断,锁超时, 公平性 等相关的知识点, 并且举了两个例子来演示ReentrantLock的用法。

可重入

定义

代码

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.joshua.reentrant8;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joshua.H.Brooks
* @description 演示ReentrantLock
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_1")
public class ReentrantDemo_1 {
public static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
lock.lock();
try {
log.debug("entered main");
m1();
} finally {
lock.unlock();
}

}

private static void m1() {
lock.lock();
try {
log.debug("entered m1");
m2();
} finally {
lock.unlock();
}
}

private static void m2() {
lock.lock();
try {
log.debug("entered m2");
} finally {
lock.unlock();
}
}
}

测试

确实可重入,并且finally也是按倒序先执行m2的, 再执行m1的finnaly 最后main的finally释放锁。

Screenshot 2022-05-11 at 08.32.10

可打断

定义

代码

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
package com.joshua.reentrant8;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joshua.H.Brooks
* @description 演示ReentrantLock的可打断特性
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_2")
public class ReentrantDemo_2 {
public static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
//如果没有竞争, 此方法就会获取lock对象的锁
//如果有竞争就会进入阻塞队列。可以被其他线程用interrupt方法打断
log.debug("尝试获取锁");
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("没有获取到锁,返回");
return;
}
try {
log.debug("获取到锁");
} finally {
lock.unlock();
}
}, "t1");
t1.start();

}
}

上面的代码是t1线程没有被其他线程打断,执行能正常输出:

1
2
09:39:09.375 [t1] DEBUG c.ReentrantDemo_2 - 尝试获取锁
09:39:09.376 [t1] DEBUG c.ReentrantDemo_2 - 获取到锁

改造代码 添加打断的逻辑:

改造一: 加了打断

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
package com.joshua.reentrant8;

import com.joshua.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joshua.H.Brooks
* @description 演示ReentrantLock的可打断特性,加了打断
* 改造一: 加了打断
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_2")
public class ReentrantDemo_2 {
public static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
//如果没有竞争, 此方法就会获取lock对象的锁
//如果有竞争就会进入阻塞队列。可以被其他线程用interrupt方法打断
log.debug("尝试获取锁");
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("没有获取到锁,返回");
return;
}
try {
log.debug("获取到锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
t1.start();

Sleeper.sleep(1_000);
log.debug("即将打断t1");
t1.interrupt();
}
}

改造二: lock.lockInterruptibly(); 换成lock.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
package com.joshua.reentrant8;

import com.joshua.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joshua.H.Brooks
* @description 演示ReentrantLock的可打断特性
* 改造二: lock.lockInterruptibly(); 换成lock.lock();
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_2")
public class ReentrantDemo_2 {
public static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("尝试获取锁");
lock.lock();
try {
log.debug("获取到锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
t1.start();

Sleeper.sleep(1_000);
log.debug("即将打断t1");
t1.interrupt();
}
}

测试

改造一 代码执行结果(可打断锁被打断,抛异常):

可打断测试

改造二 代码执行结果(产生死锁):

Screenshot 2022-05-11 at 10.11.48

对比改造一和改造二的两点总结:

  • lock.lockInterruptibly()和lock.lock(); 前者可打断后者不可打断。
  • 可打断锁会允许打断并抛异常,不可打断则容易进入死锁。

锁超时

lock.tryLock() 有几个重载方法, 表示超时的参数限制

代码

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.joshua.reentrant8;

import com.joshua.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

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

/**
* @author Joshua.H.Brooks
* @description 演示ReentrantLock的锁超时
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_3")
public class ReentrantDemo_3 {
public static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
t0();
// t1();
// t2();
// t3();

}

private static void t3() {
Thread t1 = new Thread(() -> {
log.debug("尝试获取锁");
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
log.debug("没获取到");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.debug("拿到啦~~");
} finally {
lock.unlock();
}

}, "t1");
lock.lock();
log.debug("主线程获取了");
t1.start();
//Sleeper.sleep(2_000);
lock.unlock();
}

private static void t2() {
Thread t1 = new Thread(() -> {
log.debug("尝试获取锁");
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
log.debug("没获取到");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.debug("拿到啦~~");
} finally {
lock.unlock();
}

}, "t1");
lock.lock();
log.debug("主线程获取了");
t1.start();
}

private static void t1() {
Thread t1 = new Thread(() -> {
log.debug("尝试获取锁");
if (!lock.tryLock()) {
log.debug("没获取到");
return;
}
try {
log.debug("拿到啦~~");
} finally {
lock.unlock();
}

}, "t1");
lock.lock();
log.debug("主线程获取了");
t1.start();
}

private static void t0() {
Thread t1 = new Thread(() -> {
log.debug("尝试获取锁");
if (!lock.tryLock()) {
log.debug("没获取到");
}
try {
log.debug("拿到啦~~");
} finally {
lock.unlock();
}

}, "t1");

t1.start();
}
}

测试

t0测试结果: 说明lock.tryLock()即时生效的。 如果获取到,返回true, 反之false。

t0测试结果

t1测试结果:因为主线程先上了锁 且一直没有释放, 所以没获取到

t1测试结果

t2测试结果: !lock.tryLock(1, TimeUnit.SECONDS)表明先等1秒, 如果1秒之内锁被其他线程释放, 则能获取到, 则返回true, 否则false。

t2测试结果

t3测试结果:主线程释放了锁lock.unlock();

t3测试结果

//Sleeper.sleep(2_000); 当然如果将t3的注释行打开, 那还是获取不到, 因为释放锁的时间超过了等待时间。

ReentrantLock解决哲学家就餐问题(tryLock)

代码

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.joshua.reentrant8;


import com.joshua.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joshua.H.Brooks
* @description 演示ReentrantLock的锁超时解决哲学家就餐问题
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_4")
public class ReentrantDemo_4 {
/**
* 在学习锁超时前解决的方式是改变获取锁的顺序, 将最后
*/
public static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
Chopstick c1 = new Chopstick("1");
Chopstick c2 = new Chopstick("2");
Chopstick c3 = new Chopstick("3");
Chopstick c4 = new Chopstick("4");
Chopstick c5 = new Chopstick("5");
new Philosopher("苏格拉底", c1, c2).start();
new Philosopher("柏拉图", c2, c3).start();
new Philosopher("亚里士多德", c3, c4).start();
new Philosopher("赫拉克利特", c4, c5).start();
new Philosopher("阿基米德", c5, c1).start();


}
}

@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread {
Chopstick left;
Chopstick right;

public Philosopher(String name, Chopstick left, Chopstick right) {
super(name);
this.left = left;
this.right = right;
}

@Override
public void run() {
while (true) {
if (left.tryLock()) {
try {
if (right.tryLock()) {
try {
eat();
} finally {
right.unlock();
}
}
} finally {
left.unlock();
}
}
}
}

private void eat() {
log.debug("双筷在手,开始干饭~~~~");
Sleeper.sleep(100);
}
}

@Slf4j(topic = "c.Chopstick")
class Chopstick extends ReentrantLock {
private String name;

public Chopstick(String name) {
this.name = name;
}

@Override
public String toString() {
return "Chopstick{" +
"name='" + name + '\'' +
'}';
}
}

结果

ReentrantLock解决哲学家就餐问题(tryLock)

总结

哲学家就餐问题的根本在于大家各持有一只筷子(锁), 等待邻座释放(锁)。那么RenentrantLock的锁超时就可以控制获取不到筷子时将其释放掉。 该功能就避免了死锁。 而且这种方式优于之前改造加锁顺序new Philosopher("阿基米德", c5, c1).start();的方式。因为不会造成某个线程获取锁机会明显过大(小)的现象。

公平性

ReentrantLock 默认时非公平的, 可以通过调用有参构造修改为公平锁。 公平锁的意思是 会按照进入等待队列的顺序获取到锁资格。

源码掠影

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}

/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

代码(不是总能复现仅供参考)

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
package com.joshua.reentrant8;

import com.joshua.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joshua.H.Brooks
* @description 演示ReentrantLock的公平性
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_5")
public class ReentrantDemo_5 {
// public static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.lock();
for (int i = 0; i < 5000; i++) {
new Thread(() -> {
lock.lock();
try{
System.out.println(Sleeper.getThreadName()+"running...");
}finally {
lock.unlock();
}
}, "t"+i).start();
}
Sleeper.sleep(1_000);
new Thread(() -> {
System.out.println(Sleeper.getThreadName()+"start~~~~~~~~~~~~~~~~~~~~~~~");
lock.lock();
try{
System.out.println(Sleeper.getThreadName()+"running~~~~~~~~~~~~~~~~~~~~~~~");
}finally {
lock.unlock();
}
}, "强行插入").start();

lock.unlock();
}

}

ReentrantLock lock = new ReentrantLock(fairness); 如果是false, 则强行插入强行插入running~~~~~~~~~~~~~~~~~~~~~~~

可能在中间输出, 但是改成true之后, 则一定在最后输出。不是总能复现仅供参考,后续源码会解释

多条件

使用

代码(对比wait-notify例子)

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package com.joshua.reentrant8;

import com.joshua.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

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

/**
* 演示ReentrantLock 来实现之前外卖-等烟 的例子。
*/

@Slf4j(topic = "c.ReentrantDemo_6")
public class ReentrantDemo_6 {
public static final Object room = new Object();
private static boolean hasCigar = false;
private static boolean foodDelivered = false;
/**
* 获取锁
*/
static ReentrantLock lock = new ReentrantLock();
/**
* 等烟的条件
*/
static Condition con1 = lock.newCondition();
/**
* 等外卖的条件
*/
static Condition con2 = lock.newCondition();

public static void main(String[] args) {
new Thread(() -> {
lock.lock();
try {
log.debug("有烟没? [{}]", hasCigar);
while (!hasCigar) {
log.debug("没外卖, 先歇会");
try {
con1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有烟没? [{}]", hasCigar);
if (hasCigar) {
log.debug("可以开始干活了");
} else {
log.debug("没干成活");
}

} finally {
lock.unlock();
}
}, "小男").start();


new Thread(() -> {
lock.lock();
try {
log.debug("外卖送到没? [{}]", foodDelivered);
if (!foodDelivered) {
log.debug("没外卖, 先歇会");
try {
con2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("外卖送到没? [{}]", foodDelivered);
if (foodDelivered) {
log.debug("可以开始干活了");
} else {
log.debug("没干成活");
}
} finally {
lock.unlock();
}
}, "小女").start();


Sleeper.sleep(1_000);
new Thread(() -> {
lock.lock();
try {
foodDelivered = true;
log.debug("外卖到了哦");
con2.signal();
} finally {
lock.unlock();
}
}, "送外卖的").start();

Sleeper.sleep(1_000);
new Thread(() -> {
lock.lock();
try {
hasCigar = true;
log.debug("烟到了哦");
con1.signal();
} finally {
lock.unlock();
}
}, "送烟的").start();
}
}

测试结果

Screenshot 2022-05-11 at 12.07.38

Synchronized和ReentrantLock对比

同步模式之顺序控制

假设现在有这样一个场景, 两个线程t1和t2分别输出“吃了嘛您?” 和 “吃啦, 您呢?”, 需要先执行t1,再执行t2。那么为了控制这种先后次序,就有人为干预,

方法一 wait-notify

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
package com.joshua.reentrant8;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
* @author Joshua.H.Brooks
* @description 演示同步模式之顺序控制 wait-notify实现方式
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_1")
public class ReentrantDemo_7 {
public static Object obj = new Object();
static boolean t1Runned = false;

public static void main(String[] args) {

Thread t1 = new Thread(() -> {
synchronized (obj) {
log.debug("吃了嘛您嘞?");
t1Runned = true;
obj.notify();
}
}, "t1");

Thread t2 = new Thread(() -> {
synchronized (obj) {
while (!t1Runned) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}
log.debug("吃啦, 您呢");
}, "t2");
t1.start();
t2.start();

}
}

方法一 结果

方法一 结果

方法二 ReentrantLock

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
package com.joshua.reentrant8;

import lombok.extern.slf4j.Slf4j;

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

/**
* @author Joshua.H.Brooks
* @description 演示同步模式之顺序控制 ReentrantLock实现方式
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_1")
public class ReentrantDemo_8 {
public static ReentrantLock lock = new ReentrantLock();
static boolean t1Runned = false;
static Condition con = lock.newCondition();

public static void main(String[] args) {

Thread t1 = new Thread(() -> {
lock.lock();
try {
log.debug("吃了嘛您嘞?");
t1Runned = true;
con.signal();
} finally {
lock.unlock();
}
}, "t1");

Thread t2 = new Thread(() -> {
lock.lock();
try {
while (!t1Runned) {
try {
con.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
log.debug("吃啦, 您呢");
}, "t2");
t1.start();
t2.start();
}
}

方法二 结果

方法二 结果

方法三 park-unpark

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
package com.joshua.reentrant8;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.LockSupport;

/**
* @author Joshua.H.Brooks
* @description 演示同步模式之顺序控制 park-unpark实现方式
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_1")
public class ReentrantDemo_9 {
public static void main(String[] args) {
Thread t2 = new Thread(() -> {
LockSupport.park();
log.debug("吃啦, 您呢");
}, "t2");
Thread t1 = new Thread(() -> {
log.debug("吃了嘛您嘞?");
LockSupport.unpark(t2);

}, "t1");
t1.start();
t2.start();
}
}

方法三 结果

方法三 结果

交替输出需求

需求 :

Wait-Notify 实现

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
56
57
58
59
60
61
62
63
64
65
package com.joshua.reentrant8;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.LockSupport;

/**
* 交替输出需求: t1线程打印a,t2线程打印b,t3线程打印c,依次交替各打印5次。即abcabcabcabcabc
* @author Joshua.H.Brooks
* @description Wait-Notify 完成交替输出需求
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_10")
public class ReentrantDemo_10 {
public static void main(String[] args) {
WaitNotify wn = new WaitNotify(1, 5);
new Thread(() -> {
wn.print("a",1,2);
}, "t1").start();
new Thread(() -> {
wn.print("b",2,3);
}, "t2").start();
new Thread(() -> {
wn.print("c",3,1);
}, "t3").start();
}
}

class WaitNotify {
private int flag;
private int loopNumber;

public WaitNotify(int flag, int loopNumber) {
this.flag = flag;
this.loopNumber = loopNumber;
}

/**
*
* a, 1, 2
* b, 2, 3
* c, 3, 1
* @param str
* @param waitFlag 当前等待标记,如果与flag一致,则打印,否则等待
* @param nextFlag 下一个标记,
* 只要按照上面的顺序控制了顺序。
*/
public void print(String str, int waitFlag, int nextFlag) {
for (int i = 0; i < loopNumber; i++) {
synchronized (this) {
while (flag != waitFlag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(str);
flag = nextFlag;
this.notifyAll();
}
}

}
}

Wait-Notify 实现交替输出

await-signal实现

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.joshua.reentrant8;

import com.joshua.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

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

/**
* 交替输出需求: t1线程打印a,t2线程打印b,t3线程打印c,依次交替各打印5次。即abcabcabcabcabc
*
* @author Joshua.H.Brooks
* @description ReentrantLock 完成交替输出需求
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_11")
public class ReentrantDemo_11 {
public static void main(String[] args) {
AwaitSignal awaitSignal = new AwaitSignal(5);
Condition a = awaitSignal.newCondition();
Condition b = awaitSignal.newCondition();
Condition c = awaitSignal.newCondition();

new Thread(() -> {
awaitSignal.print("a", a, b);
}, "t1").start();

new Thread(() -> {
awaitSignal.print("b", b, c);
}, "t2").start();

new Thread(() -> {
awaitSignal.print("c", c, a);
}, "t3").start();
//主线程控制从a开始所以先唤醒a
Sleeper.sleep(1_000);
awaitSignal.lock();
try {
log.debug("开始");
a.signal();
} finally {
awaitSignal.unlock();
}

}

}

class AwaitSignal extends ReentrantLock {
private int loopNumber;

public AwaitSignal(int loopNumber) {
this.loopNumber = loopNumber;
}

public void print(String str, Condition current, Condition next) {
for (int i = 0; i < loopNumber; i++) {
lock();
try {
//t1,t2,t3三个线程一进来都调用了await,否则下一轮没法唤醒
current.await();
System.out.print(str);
next.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
unlock();
}
}
}
}

注意⚠️: 体会为什么在try后第一行就current.await();

await-signal实现交替输出

如果主线程唤醒的代码从a.signal();改成b.signal(); 则结果会变成bca连续五次:

park-unpark实现

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
56
57
58
59
60
package com.joshua.reentrant8;

import com.joshua.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
* 交替输出需求: t1线程打印a,t2线程打印b,t3线程打印c,依次交替各打印5次。即abcabcabcabcabc
*
* @author Joshua.H.Brooks
* @description ParkUnpark 完成交替输出需求
* @date 2022-05-11 08:26
*/
@Slf4j(topic = "c.ReentrantDemo_12")
public class ReentrantDemo_12 {
static Thread t1;
static Thread t2;
static Thread t3;
public static void main(String[] args) {
ParkUnpark pup = new ParkUnpark(5);
t1= new Thread(() -> {
pup.print("a",t2);
}, "t1");

t2= new Thread(() -> {
pup.print("b",t3);
}, "t2");

t3= new Thread(() -> {
pup.print("c",t1);
}, "t3");

t1.start();
t2.start();
t3.start();

log.debug("开始");
LockSupport.unpark(t1);
}

}

class ParkUnpark{
private int loopNumber;

public ParkUnpark(int loopNumber) {
this.loopNumber = loopNumber;
}

public void print(String str,Thread next){
for (int i = 0; i < loopNumber; i++) {
LockSupport.park();
System.out.print(str);
LockSupport.unpark(next);
}
}
}
-------------本文结束感谢您的阅读-------------