题目分析
完成本题所需要的条件:
- 需要两个线程,一个线程打印奇数,一个线程打印偶数;
- 当奇数打印线程在执行打印操作时,偶数打印线程需要处于阻塞状态;
- 当奇数线程打印操作完成后,需要唤醒偶数线程,并进入阻塞状态(释放锁),让偶数线程开始打印。
条件分析结果
通过分析条件2、3,可以确定是线程间的通信问题,所以首先需要建立两个线程,而且两个线程必须共享同一把锁(为了实现线程间的通信),并且需要一个标记状态来分辨哪一个线程在执行,哪一个线程需要阻塞。
(其实也可以通过判断当前打印数是否是奇数或是偶数,来判断线程的状态,本文使用的是标记来实现线程状态区分)
实现代码
为了方便分析本文把线程操作实现类放在了同一个文件中
package thread;
/**
* 线程交替打印奇偶数
* @author zcl
*
*/
// 切记线程中的通信必须是共享一把锁的
// 共享类
class Num{
// 需要打印的数字 初始化为0
int num = 0;
// 打印奇数还是偶数的判定条件,也是线程间通信的条件
// true和false的设定,根据打印奇数还是偶数开头进行设定
boolean flag = true;
}
/**
* 偶数打印线程实现类
* @author zcl
*/
class Even implements Runnable {
// 共享对象 实现锁的共享
private Num n;
public Even(Num n) {
super();
this.n = n;
}
@Override
public void run() {
try {
// 打印的边界
while (n.num < 100) {
// n为共享对象 为了实现两个线程 共享同一把锁
// 锁住对象n只有一个线程可以执行
synchronized (n) {
// 如果是true 则是开始打印偶数 因为我们初始值设置的是0
if (n.flag) {
System.out.println(Thread.currentThread().getName()+"执行,获取偶数:" + n.num);
// 打印之后对数字进行+1操作
n.num++;
// 将判定条件修改为false 目的是使当前线程等待
n.flag = false;
// 唤醒奇数线程 只有两个线程所以使用notify就够了
n.notify();
} else {
// 偶数打印线程进入阻塞状态 释放锁(奇数打印线程开始拿到锁进行打印)
n.wait();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 奇数打印线程实现类(分析过程如偶数打印线程)
* @author zcl
*/
class Odd implements Runnable {
private Num n;
public Odd(Num n) {
super();
this.n = n;
}
@Override
public void run() {
try {
while (n.num < 100) {
synchronized (n) {
// 此处一开始flag我们设定为true,所以如果奇数线程先执行,会直接进入else,成为阻塞状态
if (!n.flag) {
System.out.println(Thread.currentThread().getName()+"执行,获取奇数:" + n.num);
n.num++;
n.flag = true;
// 唤醒偶数线程
n.notify();
} else {
n.wait();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadNumber {
public static void main(String[] args) {
// 新建一个线程共享对象
Num num = new Num();
Odd odd = new Odd(num);
Even even = new Even(num);
// 建立奇数线程
Thread t1 = new Thread(odd);
// 建立偶数线程
Thread t2 = new Thread(even);
t1.setName("奇数线程");
t2.setName("偶数线程");
// 分别开启两个个线程
t1.start();
t2.start();
}
}
控制台打印结果
偶数线程执行,获取偶数: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
总结
交替执行线程,瞄准线程的通信准没错。