黑马程序员 ---- 线程间通信

  ------- android培训java培训、期待与您交流! ----------


例1:问题的引出。(共享数据没有同步) 【误】


class P
{
	String name;
	String sex;
}

class Producer implements Runnable
{
	P q = null;
	
	public Producer(P q)
	{
		this.q = q;
	}
	
	public void run()
	{
		int i = 0;
		while(true)
		{
			if(i==0)
			{
				q.name = "Zhang";
				q.sex = "mail";
			}
			else
			{
				q.name = "LI";
				q.sex = "femail";
			}
			i = (i+1)%2;		// 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。
		}
	}
}

class Consumer implements Runnable
{
	P q = null;
	
	public Consumer(P q)
	{
		this.q = q;
	}
	
	public void run()
	{
		while(true)
		{
			System.out.println(q.name+"---------"+q.sex);
		}
	}
}

class Test
{
	public static void main(String[] args)
	{
		P k = new P();
		
		Producer i = new Producer(k);
		Consumer o = new Consumer(k);
		
		
		Thread t1 = new Thread(i);
		Thread t2 = new Thread(o);
		
		t1.start();
		t2.start();
		
		//new Thread(new Producer(q)).start();
		//new Thread(new Consumer(q)).start();
	}
	
}

运行结果:   该程序有问题。 没有同步操作共享数据的代码。可能会出现 name 与 sex 不对应的情况。


例2: 问题的解决。(共享数据同步,加入睡眠唤醒机制,但代码没有优化)     【正】

class P
{
	String name;
	String sex;
	
	boolean flag = false;
}

class Producer implements Runnable
{
	P q = null;
	
	public Producer(P q)
	{
		this.q = q;
	}
	
	public void run()
	{
		int i = 0;
		while(true)
		{
			synchronized(q)     // 锁可以是 Producer.class 、 Consumer.class 、 q 、 Test.class  这些都是唯一的。
			{	
				if(q.flag)
					try{q.wait();}catch(Exception e){}
				if(i==0)
				{
					q.name = "Zhang";
					q.sex = "mail";
				}
				else
				{
					q.name = "LI";
					q.sex = "femail";
				}
				i = (i+1)%2;		// 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。
				q.flag = true;
				q.notify();
			}
		}
	}
}

class Consumer implements Runnable
{
	P q = null;
	
	public Consumer(P q)
	{
		this.q = q;
	}
	
	public void run()
	{
		while(true)
		{

			synchronized(q)
			{
				if(!q.flag)
					try{q.wait();}catch(Exception e){}
				
				System.out.println(q.name+"---------"+q.sex);
				q.flag = false;
				q.notify();
			}
		}	
	}
}

class Test
{
	public static void main(String[] args)
	{
		P k = new P();
		
		Producer i = new Producer(k);
		Consumer o = new Consumer(k);
		
		
		Thread t1 = new Thread(i);
		Thread t2 = new Thread(o);
		
		t1.start();
		t2.start();
		
		//new Thread(new Producer(q)).start();
		//new Thread(new Consumer(q)).start();
	}
	
}

运行结果: 该程序正常运行,并得出正确结果。对应的两组 name 、 sex 交替出现。


范例分析: 1. wait(). notify(). notifyAll() 都是用在同步中,因为要对持有的监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。

                 2. 为什么这些操作线程的方法要定义 Object 类中呢
                     因为这些方法在操作同步中线程时,都必须要标识他们所操作的线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上notify唤醒。不可以
               对不同锁中的线程唤醒, 也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object 类中。 


例3: 代码优化。(优化代码,但只是用于一个消费者,一个生产者,多个消费者、生产者就不适用了)     【正】

class P
{
	private String name;
	private String sex;
	private boolean flag = false;
	
	public synchronized void set(String name, String sex)
	{
		if(flag)
			try{wait();}catch(Exception e){}
		
		this.name = name;
		this.sex = sex;		
		
		flag = true;
		this.notify();
		
	}
	
	public synchronized void out()
	{
		if(!flag)
			try{wait();}catch(Exception e){}
		System.out.println(name+"\t"+sex);
		flag = false;
		this.notify();
	}
	
}

class Producer implements Runnable
{
	P q = null;
	
	public Producer(P q)
	{
		this.q = q;
	}
	
	public void run()
	{
		int i = 0;
		while(true)
		{
				if(i==0)
				{
					q.set("zhang","mail");
				}
				else
				{
					q.set("LI","femail");
				}
				i = (i+1)%2;		// 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。
		}
	}
}

class Consumer implements Runnable
{
	P q = null;
	
	public Consumer(P q)
	{
		this.q = q;
	}
	
	public void run()
	{
		

			while(true)
			{
				q.out();
				
			}	
	}
}

class Test
{
	public static void main(String[] args)
	{
		P q = new P();
		/*
		Producer i = new Producer(k);
		Consumer o = new Consumer(k);
		
		
		Thread t1 = new Thread(i);
		Thread t2 = new Thread(o);
		
		t1.start();
		t2.start();
		*/
		new Thread(new Producer(q)).start();
		new Thread(new Consumer(q)).start();
	}
	
}

运行结果: 该程序正确,并得出正确结果。

范例分析: 优点: 在例2 的基础上采取了优化措施,并封装了代码私有后更简洁更安全。
           缺点: 只是用于一个生产者,一个消费者。
             

例4:延续上个程序的需求,但要加入多个生产者和多个消费者.


class P
{
	private String name;
	private int count = 1;
	private boolean flag = false;
	
	public synchronized void set(String name)
	{
		while(flag)            // 此处用 while 循环是为了让判断过并在此等待的线程,在被唤醒时再判断一次,以免在唤醒时被同是生产者或同是消费者给唤醒。                 
			try{wait();}catch(Exception e){}
		
		this.name = name+"....."+count++;
		System.out.println(Thread.currentThread().getName()+"-----Producer--------"+count);
		
		flag = true;
		this.notifyAll();       // 一个线程在执行完后,为了不唤醒被类中的线程,而让所有线程都被唤醒,让后用 while 再次判断是否该唤醒需要唤醒的线程。 
                                // 当只有一个 Producer 和 一个 Consumer 时, 可以用 notify() ,而当有多个时,用 notifyAll(),来唤醒所有在等待的线程。    
	}                           // 如果这里使用 notify ,程序在运行一段时间后自动就停止了,因为没有被唤醒的线程没有执行权,而有执行权的线程却没被唤醒。
	
	public synchronized void out()
	{
		while(!flag)
			try{wait();}catch(Exception e){}
		System.out.println(Thread.currentThread().getName()+"+++++++++++++Consumer+++++"+count);
		flag = false;
		this.notifyAll();
	}
	
}

class Producer implements Runnable
{
	P q = null;
	
	public Producer(P q)
	{
		this.q = q;
	}
	
	public void run()
	{
		int i = 0;
		while(true)
		{
			q.set("水果糖");
		}
	}
}

class Consumer implements Runnable
{
	P q = null;
	
	public Consumer(P q)
	{
		this.q = q;
	}
	
	public void run()
	{
		

			while(true)
			{
				q.out();
				
			}	
	}
}

class Test
{
	public static void main(String[] args)
	{
		P q = new P();
		
		Producer p = new Producer(q);
		Consumer c = new Consumer(q);
		
		Thread t1 = new Thread(p);          // 定义了四个生产者和四个消费者
		Thread t2 = new Thread(p);
		Thread t3 = new Thread(c);
		Thread t4 = new Thread(c);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		
	}
	
}

运行结果: 该程序正确,并得出正确结果。




范例分析 ;  优点: 在例3的基础上让程序可以同时有多个生产者和多个消费者。
             缺点: 不适用于一个消费者,一个生产者。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值