zoukankan      html  css  js  c++  java
  • JAVA学习第二十六课(多线程(五))- 多线程间的通信问题

    一、线程间的通信


    实例代码:

    需求是:输入一个姓名和性别后,就输出一个姓名和性别

    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 = "BLF";
    					r.sex = "male";
    				}
    				else {
    					r.name = "妮妮妮妮";
    					r.sex = "female";
    				}
    				x = (++x)%2;
    			}
    		}
    	}
    }
    
    class Output implements Runnable
    {
    	Resource r;
    	public Output(Resource r) {
    		// TODO Auto-generated constructor stub
    		this.r = r;
    	}
    	public void run() 
    	{
    		while(true)
    		{
    			synchronized (r) //输入和输出都是应用同一个锁
    			{
    				System.out.println(r.name+"..."+r.sex);
    			}
    		}
    	}
    }
    public class Main 
    {
    	public static void main(String[] args)
    	{
    		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();
    	}
    }
    

    上述代码尽管攻克了。多线程处理同一资源问题,可是出现了一个问题就是,打印非常多一个人名和性别后再打印还有一个,无法实现交替输出。原因:输入线程获取运行权后,不会运行一次,输入姓名和性别,输入线程还拥有运行权。一直赋值。等到切换到输出运行权后,输出最后输入的姓名和性别。可是输出线程也不会仅仅输出一次,一直输出,所以出现打印非常多同一姓名和性别的问题


    二、线程的等待/唤醒机制:

    用一个boolean值推断是否有数据,有就不放。没有就放

    输入线程运行时推断是否有数据,有。就释放运行权,再释放运行资格,进入冻结状态。输出线程运行。输出后boolean值置为false


    Input:
             if(flag)//有值
               wait();//当前冻结,切换输出线程
             flag = true;
             notify();
    
    Output:
             if(!flag)
           wait();
             flag = false;//flag置为假。输出完成
          notify();//唤醒输入线程

    等待/唤醒机制:

    方法:

    1.wait();//使线程进入冻结状态,CPU释放运行权和运行资格,被wait()的线程会被存储到线程池中
    2.notify();//唤醒线程池中的随意的一个线程
    3.notifyAll();//唤醒全部线程,使之处于运行状态或暂时堵塞状态,总之,使其具备运行资格

    这些方法必须定义在同步中。这些方法是用于操作线程状态的方法,所以必须明白在哪个锁上的线程

    (wait()A锁的线程。仅仅能用A锁的notify唤醒A锁的线程。

    了解:而wait()等方法是定义在Object类中的。这些方法是监视器的方法。而监视器呢。就能够理解为锁,控制哪个锁所属下的线程,而锁又能够是随意的,而全部的类都是继承于Object类的,所以wait()等类是定义在Object里


    下述代码就攻克了上述代码的问题,实现了输入输出线程的交替运行

    class Resource extends Object
    {
    	String name;
    	String 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) 
    			{
    				if(r.flag)
    				{
    					try {
    						r.wait();
    					} catch (Exception e) {
    						// TODO: handle exception
    					}
    					
    				}
    					
    				if(x==0)
    				{
    					r.name = "BLF";
    					r.sex = "male";
    				}
    				else {
    					r.name = "妮妮妮妮";
    					r.sex = "female";
    				}
    				r.flag = true;
    				r.notify();
    				x = (++x)%2;
    			}
    		}
    	}
    }
    
    class Output implements Runnable
    {
    	Resource r;
    	public Output(Resource r) {
    		// TODO Auto-generated constructor stub
    		this.r = r;
    	}
    	public void run() 
    	{
    		
    		while(true)
    		{
    			synchronized (r) 
    			{
    				if(!r.flag)
    				{
    					try {
    						r.wait();
    					} catch (Exception e) {
    						// TODO: handle exception
    					}
    				}
    					
    					
    				System.out.println(r.name+"..."+r.sex);
    				r.flag = false;
    				r.notify();
    			}
    		}
    	}
    }
    public class Main 
    {
    	public static void main(String[] args)
    	{
    	
    		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();
    	}
    }
    

    三:等待唤醒机制代码优化


    资源中的成员为了可控,应该为私有的。对外提供方法

    所以同步的操作。就在Resource类中运行,应用同步函数

    class Resource extends Object
    {
    	private String name;
    	private String sex ;
    	private boolean flag = false;
    	public synchronized void set(String name,String sex) 
    	{
    		// TODO Auto-generated constructor stub
    		if(flag)
    		{
    			try {
    				this.wait();
    			} catch (Exception e) {
    				// TODO: handle exception
    				
    			}
    		}
    		
    		this.name = name;
    		this.sex = sex;
    		
    		flag = true;
    		this.notify();
    	}
    	public synchronized void out()
    	{
    		if(!flag)
    		{
    			try {
    				this.wait();
    			} catch (Exception e) {
    				// TODO: handle exception
    			}
    			System.out.println(name+"  :  "+sex);
    			flag = false;
    			this.notify();
    		}
    	}
    }
    class Input implements Runnable
    {
    	Resource r;
    	Input(Resource r)
    	{
    		this.r = r;
    	}
    	public void run()
    	{
    		int x = 0;
    		while(true)
    		{
    			if(x==0)
    			{
    				r.set("BLF", "male"); 				
    			}
    			else 
    			{
    				r.set("妮妮妮妮", "female");
    			}			
    			x = (++x)%2;
    			
    		}
    	}
    }
    class Output implements Runnable
    {
    	Resource r;
    	public Output(Resource r) {
    		// TODO Auto-generated constructor stub
    		this.r = r;
    	}
    	public void run() 
    	{
    		while(true)
    		{
    			r.out();
    		}
    	}
    }
    public class Main 
    {
    	public static void main(String[] args)
    	{
    	
    		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();
    	}
    }
    


  • 相关阅读:
    如何使用类
    面向过程编程与面向对象优缺点
    生成器和迭代器的藕断丝连
    三元运算
    python 和pycharm 安装
    命令提示符玩法
    模块
    包(package)
    logging模块
    1964、1969和1972---------为什么中国互联网大佬出生在这3个年份
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/7272766.html
Copyright © 2011-2022 走看看