线程间通信
传送门
https://blog.nowcoder.net/n/29dfac7b41b54be180aa4c2eb68dacb6
问题描述
有4个线程和1个公共的字符数组。线程1的功能就是向数组输出A,线程2的功能就是向字符输出B,线程3的功能就是向数组输出C,线程4的功能就是向数组输出D。要求按顺序向数组赋值ABCDABCDABCD,ABCD的个数由线程函数1的参数指定。
示例1 输入 10 输出 ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD
解题思路
线程交替打印
线程交替打印需要用到线程通信的知识。其他的方法我不太了解,首先想到的就是用Condition来实现线程之间的通信。刚开始写好代码,本地运行,多组测试结果什么的都正确,但是一到线上运行,打印的结果就是乱的,之前从来没有遇到过这种情况,想了我一个多小时。最后突然想到测试用例输入的时候和在本地运行输入的情况有点不一样。因为需要多组测试,所以需要定义一个while(scanner.hasNext()){...}的循环体。本地测试由于是我们手动输入的,在下一次输入之前,前一次的打印结果就已经全部结束了(四个线程已经运行结束)。但是线上是机器测试的,可能前一次的测试还没打印结束,下一次的测试又开始了,所以会导致两次的测试数据会穿插在一起。
知道了原因,就好办了,只要做到每一次测试打印结束后while(){}循环才能结束,首先想到的就是用CountdownLatch。每次while循环开始就定义一个countdownLatch(4),每个线程结束后计数器就减一,减到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
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
import java.util.Scanner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 多线程,线程交替打印ABCDABCD */ public class Test49 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt(); CountDownLatch countDownLatch = new CountDownLatch( 4 ); AlternativePrint alternativePrint = new AlternativePrint(); //创建四个线程 new Thread( new Runnable() { @Override public void run() { try { for ( int i = 0 ; i < n; i++) { alternativePrint.printA(); } } finally { countDownLatch.countDown(); } } }).start(); new Thread( new Runnable() { @Override public void run() { try { for ( int i = 0 ; i < n; i++) { alternativePrint.printB(); } } finally { countDownLatch.countDown(); } } }).start(); new Thread( new Runnable() { @Override public void run() { try { for ( int i = 0 ; i < n; i++) { alternativePrint.printC(); } } finally { countDownLatch.countDown(); } } }).start(); new Thread( new Runnable() { @Override public void run() { try { for ( int i = 0 ; i < n; i++) { alternativePrint.printD(); } } finally { countDownLatch.countDown(); } } }).start(); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(); } } } class AlternativePrint { private Lock lock = new ReentrantLock(); private Condition conditionA = lock.newCondition(); private Condition conditionB = lock.newCondition(); private Condition conditionC = lock.newCondition(); private Condition conditionD = lock.newCondition(); private int number = 1 ; void printA() { lock.lock(); try { if (number != 1 ) { conditionA.await(); } System.out.print( "A" ); //"A"打印结束,标记置为2,并唤醒打印"B"的线程 number = 2 ; conditionB.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } void printB() { lock.lock(); try { if (number != 2 ) { conditionB.await(); } System.out.print( "B" ); //"B"打印结束,标记置为3,并唤醒打印"C"的线程 number = 3 ; conditionC.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } void printC() { lock.lock(); try { if (number != 3 ) { conditionC.await(); } System.out.print( "C" ); //"C"打印结束,标记置为4,并唤醒打印"D"的线程 number = 4 ; conditionD.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } void printD() { lock.lock(); try { if (number != 4 ) { conditionD.await(); } System.out.print( "D" ); //"D"打印结束,标记置为1,并唤醒打印"A"的线程 number = 1 ; conditionA.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } |