线程之间的通信

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();
				
			}
		}
	}
	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值