14-1,多线程-线程间通信示例
1,什么是多线程间通讯?
多线程通讯:多个线程在处理同一个资源,但是任务不同。
比如说:有一堆煤(资源),一辆卡车向里放煤(Input),一辆卡车向外取煤(output),放煤和取煤的任务不同,但操作的是同一个资源。
由于有两个任务,所以要定义两个run方法。
2,以下面代码说明。
定义name和sex变量,实现交替输出不同信息的功能。定义两个run方法,一个输入名字,另一个打印名字,因为name和sex是资源,将之封装成一个类,两个run方法分别创建一个类,代码如下:
//资源
class Resource {
String name;
String sex;
}
class Input implements Runnable {
Resource r;
//资源是一开始就有的,定义在构造器中
Input(Resource r) {
this.r = r;
}
public void run() {
int x = 0;
while(true) {
//这里会出现安全问题
synchronized(r) {
if(x == 0) {
r.name = "mike";
r.sex = "nan";
} else {
r.name = "丽丽";
r.sex = "女女女女女";
}
}
//实现x在0和1之间的变化,保证两个赋值都能执行到
x = (x + 1) % 2;
}
}
}
class Output implements Runnable {
Resource r;
Output(Resource r) {
this.r = r;
}
public void run() {
while(true) {
synchronized(r) {
//这句是把赋的值输出出来,跟赋值有关,也应该放在同步代码块中
System.out.println(r.name + "..." + r.sex);
}
}
}
}
class ResourceDemo {
public static void main(String[] args) {
//创建资源
/*
创建资源,并用Input和Output传入进去,保证两个run操作的是同一个Resource对象。
并且保证了两个线程用的是同一个锁。
*/
Resource r = new Resource();
//创建任务
Input in = new Input(r);
Output out = new Output(r);
//创建线程
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
说明:不加同步时可能会出现mike...女女女女女,或,丽丽...nan的情况,因为在进行完一次赋值后,切换到另一种赋值时,如赋了mike...nan,在要赋丽丽,女女女女女时,CPU在只赋了丽丽后,就切走了,这时sex还是nan,所以出现了t2输出丽丽...nan的情况,加入同步代码块就能解决。
锁的问题:如果两个线程不用同一个锁是不能解决问题的,所以在main中创建一个Resource对象,把r传入,这样两个线程就能用同一个锁了。
14-2,线程间通信,等待唤醒机制
1,上一个例子中,我们希望,输出完mike...nan后就输出丽丽...女女女女女,再输出mike...nan这样。
2,等待,唤醒机制
涉及的方法:
(1)wait():让线程处于冻结状态,被wait的线程会被存储在线程池中。
(2)notify():唤醒线程池中的一个线程(任意)。
(3)notifyAll():唤醒线程池中的所有线程。
这些方法都必须被定义在同步中,因为这些方法是用于操作线程状态的方法,必须要明确操作的是哪个锁上的线程。
3,为什么操作线程的方法wait,notify,notifyAll定义在了Object中?
因为这些方法是监视器的方法(锁),监视器其实就是锁,锁可以是任意对象,任意对象调用的方法一定定义在Object中。
4,以代码说明14-1中程序实现1中的需求。
//资源
class Resource {
String name;
String sex;
//用于判断name和sex的值是否为空
boolean flag = false;
}
class Input implements Runnable {
Resource r;
//资源是一开始就有的,定义在构造器中
Input(Resource r) {
this.r = r;
}
public void run() {
int x = 0;
while(true) {
//这里会出现安全问题
synchronized(r) {
//第一次为false,不执行
if(r.flag) {
try{
r.wait();
} catch(InterruptedException e) {}
}
if(x == 0) {
r.name = "mike";
r.sex = "nan";
} else {
r.name = "丽丽";
r.sex = "女女女女女";
}
//name和sex已经赋值,不为空,置为true,t2可以从中取值输出
r.flag = true;
//唤醒t2线程
r.notify();
}
//实现x在0和1之间的变化,保证两个赋值都能执行到
x = (x + 1) % 2;
}
}
}
class Output implements Runnable {
Resource r;
Output(Resource r) {
this.r = r;
}
public void run() {
while(true) {
synchronized(r) {
//flag已经是true,!flag是false,第一次不执行
if(!r.flag) {
try{
r.wait();
}catch(InterruptedException e) {}
}
//这句是把赋的值输出出来,跟赋值有关,也应该放在同步代码块中
System.out.println(r.name + "..." + r.sex);
//输出一次把flag置为flase,防止继续输出mike...nan,
//因为若t2继续拿着执行权,!flag为true,t2会被wait。
r.flag = false;
r.notify();
}
}
}
}
class ResourceDemo {
public static void main(String[] args) {
//创建资源
/*
创建资源,并用Input和Output传入进去,保证两个run操作的是同一个Resource对