package com.me.threadtest;
/**
*
* 线程之间的通信(等待唤醒机制):多个线程在操作同一个资源(共享资源),
* 但是操作的动作不同。
*/
public class ThreadCommunication {
public static void main(String[] args) {
Student student = new Student();
//创建线程对象
Input input = new Input(student);
Output output = new Output(student);
//启动线程
new Thread(input).start();
new Thread(output).start();
}
}
class Student {
private String name;
private String sex;
public boolean flag = false; //初始值为false,表示没有赋值
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the sex
*/
public String getSex() {
return sex;
}
/**
* @param sex the sex to set
*/
public void setSex(String sex) {
this.sex = sex;
}
}
class Input implements Runnable {
/**
* Student是共享的资源,要保证多线程共享同一个资源,否则谈不上线程之间的通信,
* 所以这里不能new一个Student的实例,否则不是同一个对象(资源)。
*/
private Student student;
Input(Student student) {
this.student = student;
}
@Override
public void run() {
int x = 0;
/**
* 1、当多个线程同时操作一个资源时,会出现数据的错乱;
* 线程1执行时为student对象name赋值后,
* 准备为sex赋值,此时线程2获得了cpu执行权,导致
* 线程1 还没有完全赋完值时线程2就执行输出,从而数据错乱,
* rohan---》女 , 晶晶靓----》man
*
* 2、解决方法:线程同步,为共享资源加同步代码块。
* 这虽然解决了多线程安全问题,但是打印时输出了一大片相同的信息,
* 出现原因:线程1获得执行权后,开始为student赋值,线程执行完后,
* 有可能会再次获的cpu执行权,也就是会重复赋值,后面的把前面的覆盖掉。
* 当线程2获得cpu执行权后,就打印输出,同理,线程2也有可能会再次获得
* 执行权,所以会打印出一大片相同的信息。
*
* 3、需求:需要两个线程交替打印输出信息,怎么做?
* 使用等待唤醒机制:
* 定义一个flag标记,当flag=false时,表示没有为student对象属性赋值;
* 这时线程1执行赋值,执行完后将flag=true,表示已经赋值过,线程1唤醒notify()线程2执行,
* 线程1wait()等待,线程2获得执行权,读取数据输出,执行完后将 flag=false,
* 线程2唤醒notify()线程1,然后线程2wait()等待。。。。
* 以此交替执行线程。
*
* 4、wait() notify() notifyAll()
* 这些方法都必须使用在同步代码中,因为要对持有的监视器(锁)的线程操作,
* 而监视器是只有同步中才有,所以要使用在同步中。
*
* 5、为什么这些操作线程方法要定义在Object类中?
* 因为这些方法在操作同步中的线程时,都必须要标识他们所操作线程的持有的锁,
* 只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,
* 不可以对不同的锁中的线程进行唤醒。
* 也就是说,等待和唤醒必须是同一个锁,而锁又可以是任意对象,
* 所以可以被任意对象调用的方法只能定义在Object 类中。
*
*/
while (true) {
synchronized(student) {
//如果flag为true,表示已经刚赋值过,所以让线程等待。
if (student.flag) {
try {
/**
* wait() notify() 方法要标明所属的锁,表示让所属锁的线程执行等待,唤醒;
* 因为如果在嵌套同步中,会出现两个锁,两个线程,
* 如果不指定哪个锁,wait()将不知道要让哪个线程等待。
*/
student.wait(); //让当前输入线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (x == 0) {
student.setName("Rohan");
student.setSex("man");
} else {
student.setName("晶晶靓");
student.setSex("女");
}
x = (x + 1) % 2;
//改变flag标记的值为true,表示有值,输出线程可以来读取。
student.flag = true;
//唤醒在内存线程池中等待的输出线程
student.notify();
}
}
}
}
class Output implements Runnable {
/**
* Student是共享的资源,要保证多线程共享同一个资源,否则谈不上线程之间的通信,
* 所以这里不能new一个Student的实例,否则不是同一个对象(资源)。
*/
private Student student;
Output(Student student) {
this.student = student;
}
@Override
public void run() {
/**
* 1、当多个线程同时操作一个资源时,会出现数据的错乱;
* 线程1执行时为student对象name赋值后,
* 准备为sex赋值,此时线程2获得了cpu执行权,导致
* 线程1 还没有完全赋完值时线程2就执行输出,从而数据错乱,
* rohan---》女 , 晶晶靓----》man
*
* 2、解决方法:线程同步,为共享资源加同步代码块。
* 这虽然解决了多线程安全问题,但是打印时输出了一大片相同的信息,
* 出现原因:线程1获得执行权后,开始为student赋值,线程执行完后,
* 有可能会再次获的cpu执行权,也就是会重复赋值,后面的把前面的覆盖掉。
* 当线程2获得cpu执行权后,就打印输出,同理,线程2也有可能会再次获得
* 执行权,所以会打印出一大片相同的信息。
*
* 3、需求:需要两个线程交替打印输出信息,怎么做?
* 使用等待唤醒机制:
* 定义一个flag标记,当flag=false时,表示没有为student对象属性赋值;
* 这时线程1执行赋值,执行完后将flag=true,表示已经赋值过,线程1唤醒notify()线程2执行,
* 线程1wait()等待,线程2获得执行权,读取数据输出,执行完后将 flag=false,
* 线程2唤醒notify()线程1,然后线程2wait()等待。。。。
* 以此交替执行线程。
*
* 4、wait() notify() notifyAll()
* 这些方法都必须使用在同步代码中,因为要对持有的监视器(锁)的线程操作,
* 而监视器是只有同步中才有,所以要使用在同步中。
*
* 5、为什么这些操作线程方法要定义在Object类中?
* 因为这些方法在操作同步中的线程时,都必须要标识他们所操作线程的持有的锁,
* 只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,
* 不可以对不同的锁中的线程进行唤醒。
* 也就是说,等待和唤醒必须是同一个锁,而锁又可以是任意对象,
* 所以可以被任意对象调用的方法只能定义在Object 类中。
*
*/
while (true) {
synchronized(student) {
//如果flag为true表示有值可读取输出,为false则没有值让输出线程等待。
if (!student.flag) {
try {
student.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(student.getName() + "------>>" + student.getSex());
//输出线程读取输出值后,让flag改为false,表示没有值可读取,通知输入线程赋值
student.flag = false;
//唤醒内存线程池中等待的输入线程
student.notify();
}
}
}
}
线程之间的通信
最新推荐文章于 2022-08-13 15:32:48 发布