并发编程基础-线程间通信
# 线程间通信
线程间通信的模型有两种:
共享内存
和消息传递
。
# 1. 多线程编程步骤
# 2. synchronized 线程通信
相关方法
示例:
有两个线程,实现一个初始值是0的变量,一个线程对值+1,另一个线程对值减1。
# 虚假唤醒
问题产生 😄
wait(): 在哪行等待(睡),在哪行唤醒。
代码实现
点击查看
// 第一步 创建资源类,定义属性和操作方法
class Share {
// 初始值
private int number = 0;
// +1的方法
public synchronized void incr() throws InterruptedException {
// 第二步 判断 干活 通知
while(number != 0) { //判断number值是否是0,如果不是0,等待
this.wait(); //在哪里睡,就在哪里醒
}
// 如果number值是0,就+1操作
number++;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知其他线程
this.notifyAll();
}
// -1的方法
public synchronized void decr() throws InterruptedException {
//判断
while(number != 1) {
this.wait();
}
// 干活
number--;
System.out.println(Thread.currentThread().getName()+" :: "+number);
// 通知其他线程
this.notifyAll();
}
}
/**
* 有两个线程,实现一个初始值是0的变量,一个线程对值+1,另一个线程对值减1。
* 当添加加 CC DD 线程时候差生虚假唤醒问题
*/
public class ThreadDemo1 {
//第三步 创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
Share share = new Share();
//创建线程
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"DD").start();
}
}
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
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
结果:
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
AA :: 2
CC :: 3
BB :: 2
CC :: 3
AA :: 4
CC :: 5
BB :: 4
CC :: 5
AA :: 6
CC :: 7
BB :: 6
CC :: 7
DD :: 6
AA :: 7
DD :: 6
CC :: 7
BB :: 6
CC :: 7
DD :: 6
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
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
分析结果得出:AA线程等待的时候,有可能唤醒CC(CC也+1),结果不能保证一直为1,0,1,0
# 问题解决
查看JAVA API (opens new window),wait
方法介绍。
改进后代码实现
点击查看
class Share02 {
//初始值
private int number = 0;
//+1的方法
public synchronized void incr() throws InterruptedException {
//第二步 判断 干活 通知
while(number != 0) { //判断number值是否是0,如果不是0,等待
this.wait(); //在哪里睡,就在哪里醒
}
//如果number值是0,就+1操作
number++;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知其他线程
this.notifyAll();
}
//-1的方法
public synchronized void decr() throws InterruptedException {
//判断
while (number != 1) {
this.wait();
}
//干活
number--;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知其他线程
this.notifyAll();
}
}
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
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
结果:
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
DD :: 0
CC :: 1
BB :: 0
AA :: 1
DD :: 0
CC :: 1
BB :: 0
AA :: 1
DD :: 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 3. Lock 线程通信
Condition相关方法
代码实现
点击查看
//第一步 创建资源类,定义属性和操作方法
class Share03{
private int number = 0;
//创建Lock
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//+1
public void incr() throws InterruptedException {
//上锁
lock.lock();
try {
//判断
while (number != 0) {
condition.await();
}
//干活
number++;
System.out.println(Thread.currentThread().getName()+" :: "+number);
//通知
condition.signalAll();
}finally {
//解锁
lock.unlock();
}
}
//-1
public void decr() throws InterruptedException {
lock.lock();
try {
while(number != 1) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+" :: "+number);
condition.signalAll();
}finally {
lock.unlock();
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
Share03 share = new Share03();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"DD").start();
}
}
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
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
结果:
AA :: 1
BB :: 0
CC :: 1
DD :: 0
AA :: 1
DD :: 0
AA :: 1
DD :: 0
AA :: 1
DD :: 0
AA :: 1
DD :: 0
AA :: 1
DD :: 0
AA :: 1
DD :: 0
AA :: 1
DD :: 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4. 线程间定制化通信 😈
上面线程执行的结果顺序不一定 ,将线程按照约定的顺序定义、按照约定的顺序执行叫做线程的线程间定制化通信。
示例: A 线程打印 5 次 AA,B 线程打印 10 次 BB,C 线程打印 15 次 CC ,按照此顺序循环 10 轮
流程:
代码实现:
点击查看
package cn.cnlxc.lock;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//第一步 创建资源类
class ShareResource {
//定义标志位
private int flag = 1; // 1 AA 2 BB 3 CC
//创建Lock锁
private Lock lock = new ReentrantLock();
//创建三个condition
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
//打印5次,参数第几轮
public void print5(int loop) throws InterruptedException {
//上锁
lock.lock();
try {
//判断
while (flag != 1) {
//等待
c1.await();
}
//干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " :: " + i + " :轮数:" + loop);
}
//通知
flag = 2; //修改标志位 2
c2.signal(); //通知BB线程
} finally {
//释放锁
lock.unlock();
}
}
//打印10次,参数第几轮
public void print10(int loop) throws InterruptedException {
lock.lock();
try {
while (flag != 2) {
c2.await();
}
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + " :: " + i + " :轮数:" + loop);
}
//修改标志位
flag = 3;
//通知CC线程
c3.signal();
} finally {
lock.unlock();
}
}
//打印15次,参数第几轮
public void print15(int loop) throws InterruptedException {
lock.lock();
try {
while (flag != 3) {
c3.await();
}
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + " :: " + i + " :轮数:" + loop);
}
//修改标志位
flag = 1;
//通知AA线程
c1.signal();
} finally {
lock.unlock();
}
}
}
public class ThreadDemo4 {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print5(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "AA").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print10(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "BB").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print15(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "CC").start();
}
}
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
110
111
112
113
114
115
116
117
118
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
110
111
112
113
114
115
116
117
118
结果:
AA :: 1 :轮数:1
AA :: 2 :轮数:1
AA :: 3 :轮数:1
AA :: 4 :轮数:1
AA :: 5 :轮数:1
BB :: 1 :轮数:1
BB :: 2 :轮数:1
BB :: 3 :轮数:1
BB :: 4 :轮数:1
BB :: 5 :轮数:1
BB :: 6 :轮数:1
BB :: 7 :轮数:1
BB :: 8 :轮数:1
BB :: 9 :轮数:1
BB :: 10 :轮数:1
CC :: 1 :轮数:1
CC :: 2 :轮数:1
CC :: 3 :轮数:1
CC :: 4 :轮数:1
CC :: 5 :轮数:1
CC :: 6 :轮数:1
CC :: 7 :轮数:1
CC :: 8 :轮数:1
CC :: 9 :轮数:1
CC :: 10 :轮数:1
CC :: 11 :轮数:1
CC :: 12 :轮数:1
CC :: 13 :轮数:1
CC :: 14 :轮数:1
CC :: 15 :轮数:1
...
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
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
上次更新: 2023/04/09, 08:32:37