线程的等待和唤醒机制

线程间的通信:当多个线程同时处理同一个资源时,但是线程各自处理的动作(线程的任务)却不同时,例如一个线程负责变量赋值,另一个线程同时负责打印赋值的变量,可能就会出现混乱的情况
例如:

public class InputThread implements Runnable {
	// 不能创建对象,因为读取和写入的事同意对象
	// 利用构造方法,传参时将资源类的对象传入
	public void run() {
		int a = 0;
		Lock l = new ReentrantLock();
		while (true) {
	
			if (a % 2 == 0) {
				Resource.r.name = "张三";
				Resource.r.sex = "男";
			} else {
				Resource.r.name = "王五";
				Resource.r.sex = "女";
			}
			a++;
		}
	}
}

这里我偶数赋值为 张三…男,奇数赋值为 王五…女,用两个线程,一个线程赋值,一个线程输出

public class OutputThread implements Runnable {

	public void run() {
		Lock l = new ReentrantLock();
		while (true) {
			System.out.println(Resource.r.name + "..." + Resource.r.sex);
			Resource.r.falg = false;
		}
	}
}

结果就出现了"人妖"现象…
在这里插入图片描述
图中可以看出,已经完全的混乱了,张三和王五又是男又是女。

对于这种情况,Java中有一个重要的知识点,可以帮助我们解决这个问题,那就是–等待和唤醒机制,其实就是几个方法的使用。
(1) void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
(2) void notify()
唤醒在此对象监视器上等待的单个线程。
(3)void notifyAll()
唤醒在此对象监视器上等待的所有线程。

注意:这三个方法都不是Thread类中的方法,而是属于Object基础类,即每个对象都有这三个方法,因为每个对象都有锁,锁是每个对象的基础,当然操作锁的方法也是最基础了。
当需要调用以上的方法的时候,一定要对竞争资源进行加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常。

接下来,这里用一个赋值打印的例子来完成等待和唤醒机制

资源类,这里也把他写成了一个锁

package cn.itcast.LianXi;
/*
 * 资源类,作为数据的共享中心
 * 属性,姓名和年龄
 * 线程1 实现赋值
 * 线程2 实现变量的打印
 */
public class Resource {
	//变量写成公共的格式,这样方便调用
	public  String name;
	public  String sex;
	public  boolean flag = true;
	//不让new,可以避免输入输出线程同时创建对象,导致的输入和输出对象不一致
	//私有化资源类
	private Resource(){}
	//固定一个资源类的对象,不变化了
	public static final Resource r = new Resource();
}

赋值线程

package cn.itcast.LianXi;

/*
 * 线程1 用于实现对资源类的变量赋值
 *线程需要同步技术,否则有安全隐患
 *不用lock是因为,后面的wait()和notify()方法都需要同一对象锁调用
 *写一个锁对象,私有化,静态不可变
 */
public class InputThread implements Runnable {
	// 不能创建对象,因为读取和写入的事同意对象
	// 利用构造方法,传参时将资源类的对象传入
	public void run() {
		int i = 0;
		while (true) {
			// 同步中用的就是私有化的资源锁,保证了使用的是同一个锁
			synchronized (Resource.r) {
				// 利用奇偶数实现不同进程调用
				if (Resource.r.flag) {
					try {
						// 让打印线程等待
						//注意要两个线程要用同一个锁
						Resource.r.wait();
					} catch (InterruptedException e) {
					}
				}
				if (i % 2 == 0) {
					Resource.r.name = "张三";
					Resource.r.sex = "男";
				} else {
					Resource.r.name = "王五";
					Resource.r.sex = "女";
				}
				// 赋值完成,将flag置为true,目的是让打印线程的if语句完成判断,开始打印
				Resource.r.flag = false;
				// 唤醒打印线程
				Resource.r.notify();
				i++;
			}
		}
	}
}

打印线程

package cn.itcast.LianXi;

/*
 * 输出线程,只需要负责资源类变量的输出
 */
public class OutputThread implements Runnable {

	public void run() {
		while (true) {
			synchronized (Resource.r) {
				// 同步中用的就是私有化的资源锁,保证了使用的是同一个锁
				if (!Resource.r.flag) {
					try {
						// 让赋值线程等待,开始打印
						//注意要两个线程要用同一个锁
						Resource.r.wait();
					} catch (InterruptedException e) {
					}
				}
				System.out.println(Resource.r.name + "   " + Resource.r.sex);
				// 打印结束,将flag置为false,目的是让赋值线程的if完成判断,开始赋值
				Resource.r.flag = false;
				// 唤醒赋值线程
				Resource.r.notify();
			}
		}
	}

}

main

package cn.itcast.LianXi;
/*
 * 此类用于开启线程,完成输入输出
 */
public class ThreadDemo {
	public static void main(String[] args) {
		//创建输入输出实现类
		InputThread in = new InputThread();
		OutputThread out = new OutputThread();
		
		//创建线程
		Thread t_in = new Thread(in);
		Thread t_out = new Thread(out);
		
		t_in.start();
		t_out.start();
	}
}

结果如图:
在这里插入图片描述
就不会发生人妖现象了 -.-

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值