线程间的协作
线程之间如何进行协作,使得多个任务可以一起解决同一个问题。
首先出一个简单的题目,需要用到A和B两个线程来完成这个任务。解法会在本节最后
列出。
要求使用A,B两个线程按顺序打印1-100,A打印奇数,B打印偶数。
wait()与notify()
wait()
方法的作用是,让线程一直处于等待中,在等待过程中,锁是释放的。直到notify()
或notifyAll()
发生,才被唤醒。
wait()、notify()和notifyAll()
都是Object的公有方法。为什么要这样设计?因为锁也是对象的一部分。
注意
实际上,只能在同步方法或同步代码块
中调用wait()、notify()、notifyAll()
,否则会抛出IllegalMonitorStateException
异常,该异常是一种运行时异常
。
sleep()
sleep()
也会让线程进入等待中,不同的是,wait()
会释放锁,而sleep()
不会释放锁。
题目分析
可以使用wait()
和notify()
来解前面那题。
首先要想
①两个线程要协作
完成打印的任务,那么为了防止两个线程同时打印
,就应该对打印的方法加上一道synchronized锁
。
②那么synchronized锁应该就要用对象锁
(使用同步方法或同步代码块都是可以的),而不是类锁。思考下,为什么用类锁不行?
③怎么保证顺序打印呢?A、B两个线程要轮番获得锁
,也就是A先获得锁完成打印任务,紧接着下次就不能获得锁了,要由B获得,这时就可以想到让A打印完,进入等待
状态,并将锁释放
出来交给B,B获得锁之后,完成打印任务,让A恢复执行
。B再进入等待状态。
也就是
A获得锁,执行任务 ——> A唤醒B ——> A释放锁 ,进入等待——> B获得锁,执行任务 ——> B唤醒A ——> B释放锁,进入等待 ——> A获得锁,执行任务 ——> A唤醒B ——> A、进入等待 ——> B获得锁,执行任务 ——> 依次类推…
④于是就有了 synchronized { task()、notify()、wait()}
的代码思路
运行代码
public class PrintOddEven {
//定义打印的方法
public synchronized void print(String str,int num){
System.out.println(str+num);
notify();
try {
if(100 != num){
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//奇数打印线程
class Odd implements Runnable{
@Override
public void run() {
for(int i=1;i<100;i+=2){
print("奇数:",i);
}
}
}
//偶数打印线程
class Even implements Runnable{
@Override
public void run() {
for(int i=2;i<=100;i+=2){
print("偶数:",i);
}
}
}
public static void main(String[] args) {
PrintOddEven p = new PrintOddEven();
Odd odd = p.new Odd();
Even even = p.new Even();
new Thread(odd).start();
new Thread(even).start();
}
}
运行结果