JAVA从菜鸟到进阶(六)——多线程基础三 线程间通信(等待、唤醒机制)

一 线程间通信
(1)线程间通信基本介绍
我们在实际开发时,通常不只是多个线程去处理同一个任务,而是多个线程处理多个不同的任务(比如说有一堆煤,一个车往里面运煤,一个车往里面拉煤)
这就叫做线程间通信。
通常线程中通信创建方式:
①创建多个线程的公共资源
②在多个线程中创建对公共资源操作的不同任务
③开启各个线程
(2)进行协调线程间通信的几种方法
在等待唤醒机制涉及的方法:
①wait():让线程处于休眠状态(失去执行资格同时失去执行权)
②notify():唤醒线程池中的一个线程
③notifyAll():唤醒线程池中的所有线程
唤醒目的是使线程进入运行状态或者临时阻塞状态(获得执行资格,可能获得执行权)
注意事项:
1.为什么这些方法都定义在object类中?
因为这些方法是监视器(当前锁对象)的方法,锁对象可以是任何对象,而所有对象都是object类的子类。任意的锁对象都有这些方法
2.什么是线程池?
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
例子:一个老板开了一家银行,开始前来存钱的客户很少,老板雇佣了一个人,这个人是以单来收费的,后来存钱的客户变多了,如果这个人以单来收费,每一单结束后都要给员工结账,老板感到很厌倦,于是进行了优化改进雇佣一个人以每一月来收费。老板方便了,不需要频繁的进行结账。这个老板就创建了员工池。实现的资源的充分利用。
3.上述方法都要定义在同步中,因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪一个锁上的线程。

实例一:
单生产者单消费者问题

``


class Resource
{     Object obj=new Object(); 
	private String name;
	private int count=1;
	private boolean flag=false;//flag为假表示还没生产好,flag为真表示生产好了 
	
	public synchronized void Set(String name)
	{  
	     
		if(flag==true)
		{
			try {this.wait();} catch (InterruptedException e) {	}
		}
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"。。。。生产.."+this.name);
		flag=true;
		notify();
	}
	public synchronized void out()
	{   
		
		if(flag==false)
		{
			try {this.wait();} catch (InterruptedException e) {	}
		}
		
		System.out.println(Thread.currentThread().getName()+"。。。。消费.."+this.name);
	       flag=false;
	       notify();		
	}
	
}    
class Pro implements Runnable
{   
     Resource r;
	Pro(Resource r)
	{
		this.r=r;
	}
	public void run()
	{  
		while(true)
	   
			{
			r.Set("馒头");
			}
    }

}
class ConA implements Runnable
{   

    Resource r;
	ConA(Resource r)
	{
		this.r=r;
	}
	public void run()
	{  
		while(true)
	   {
			r.out();
	   }
   }

}
public  class Thread1 
 {
     public static void main (String []args)
     {
    	 Resource r=new Resource();
    	 Pro p=new Pro(r);
         ConA c=new ConA(r);
    	 Thread t0=new Thread(p);
         Thread t1=new Thread(c);
        t0.start();
        t1.start();
     }
}
运行结果
Thread-0。。。。生产..馒头3727
Thread-1。。。。消费..馒头3727
Thread-0。。。。生产..馒头3728
Thread-1。。。。消费..馒头3728
Thread-0。。。。生产..馒头3729
Thread-1。。。。消费..馒头3729
Thread-0。。。。生产..馒头3730
Thread-1。。。。消费..馒头3730
Thread-0。。。。生产..馒头3731
Thread-1。。。。消费..馒头3731
Thread-0。。。。生产..馒头3732
Thread-1。。。。消费..馒头3732
Thread-0。。。。生产..馒头3733
Thread-1。。。。消费..馒头3733
Thread-0。。。。生产..馒头3734
Thread-1。。。。消费..馒头3734
Thread-0。。。。生产..馒头3735
Thread-1。。。。消费..馒头3735
Thread-0。。。。生产..馒头3736
Thread-1。。。。消费..馒头3736
Thread-0。。。。生产..馒头3737
Thread-1。。。。消费..馒头3737
Thread-0。。。。生产..馒头3738
Thread-1。。。。消费..馒头3738
Thread-0。。。。生产..馒头3739
Thread-1。。。。消费..馒头3739
Thread-0。。。。生产..馒头3740
Thread-1。。。。消费..馒头3740
Thread-0。。。。生产..馒头3741
Thread-1。。。。消费..馒头3741
Thread-0。。。。生产..馒头3742
Thread-1。。。。消费..馒头3742
Thread-0。。。。生产..馒头3743
Thread-1。。。。消费..馒头3743
Thread-0。。。。生产..馒头3744
Thread-1。。。。消费..馒头3744
Thread-0。。。。生产..馒头3745
Thread-1。。。。消费..馒头3745
Thread-0。。。。生产..馒头3746
Thread-1。。。。消费..馒头3746
Thread-0。。。。生产..馒头3747
Thread-1。。。。消费..馒头3747
Thread-0。。。。生产..馒头3748
Thread-1。。。。消费..馒头3748
Thread-0。。。。生产..馒头3749
Thread-1。。。。消费..馒头3749
Thread-0。。。。生产..馒头3750
Thread-1。。。。消费..馒头3750
Thread-0。。。。生产..馒头3751
Thread-1。。。。消费..馒头3751
Thread-0。。。。生产..馒头3752
Thread-1。。。。消费..馒头3752
Thread-0。。。。生产..馒头3753
Thread-1。。。。消费..馒头3753
Thread-0。。。。生产..馒头3754
Thread-1。。。。消费..馒头3754
Thread-0。。。。生产..馒头3755
Thread-1。。。。消费..馒头3755
Thread-0。。。。生产..馒头3756
Thread-1。。。。消费..馒头3756
Thread-0。。。。生产..馒头3757
Thread-1。。。。消费..馒头3757
Thread-0。。。。生产..馒头3758
Thread-1。。。。消费..馒头3758
Thread-0。。。。生产..馒头3759
Thread-1。。。。消费..馒头3759
Thread-0。。。。生产..馒头3760
Thread-1。。。。消费..馒头3760
Thread-0。。。。生产..馒头3761
Thread-1。。。。消费..馒头3761
Thread-0。。。。生产..馒头3762
Thread-1。。。。消费..馒头3762
Thread-0。。。。生产..馒头3763
Thread-1。。。。消费..馒头3763
Thread-0。。。。生产..馒头3764
Thread-1。。。。消费..馒头3764
Thread-0。。。。生产..馒头3765
Thread-1。。。。消费..馒头3765
Thread-0。。。。生产..馒头3766

通过多次运行,在单生产者单消费者的情况下,多线程调度没问题

原因是,在单对单的情况下,生产消费方各自都不可能唤醒己方的线程。

实例二:
在单对多,或多消费者多生产者情况下。

public synchronized void Set(String name)
	{  
	     
		if(flag==true)
		{
			try {this.wait();} catch (InterruptedException e) {	}
		}
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"。。。。生产.."+this.name);
		flag=true;
		notify();
	}
	public synchronized void out()
	{   
		
		if(flag==false)
		{
			try {this.wait();} catch (InterruptedException e) {	}
		}
		
		System.out.println(Thread.currentThread().getName()+"。。。。消费.."+this.name);
	       flag=false;
	       notify();		
	}

假设开始线程二进入out方法中,flag为false线程二处于等待状态。接着线程三进入out方法中,接着线程三处于等待状态,接着线程一进入Set()方法中,flag改为true,唤醒线程二,线程二继续执行,接着flag=false,唤醒线程而此时线程三处于等待状态,唤醒之后不判断标记继续执行,造成多线程问题。
我们通常在判断标记时用while形式,当调用己方的线程时,又会进行一次判断。第一次改进如下:

	public synchronized void Set(String name)//第一次改进
	{  
	     
		while(flag==true)
		{
			try {this.wait();} catch (InterruptedException e) {	}
		}
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"。。。。生产.."+this.name);
		flag=true;
		notify();
	}
	public synchronized void out()
	{   
		
		while(flag==false)
		{
			try {this.wait();} catch (InterruptedException e) {	}
		}
		
		System.out.println(Thread.currentThread().getName()+"。。。。消费.."+this.name);
	       flag=false;
	       notify();		
	}
	
}    

运行结果

Thread-0。。。。生产..馒头1
Thread-1。。。。消费..馒头1
Thread-0。。。。生产..馒头2
Thread-1。。。。消费..馒头2

出乎意料的程序停止了运行,产生了死锁,是什么原因产生的呢?
可能原因是:当生产者或消费者一方全部线程到冻结状态,而另一方线程其中之一唤醒了自己方线程,再次经过标记判断导致所有线程全部冻结。
步骤1,t1,t2进入out判断标记全部都挂起
步骤2,t0进图get判断标记,正常运行标记改为true,接着to又进入,t0挂起
步骤3,t1进入,正常运行标记改为false,接着唤醒了自己方线程t2
步骤4,t2判断标记,挂起,t1判断标记挂起,程序产生死锁。
如何防止死锁的产生呢?

优化的代码如下:

public synchronized void Set(String name)
	{  
	     
		while(flag==true)  //t0s
		{
			try {this.wait();} catch (InterruptedException e) {	}
		}
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"。。。。生产.."+this.name);
		flag=true;
		notifyAll();
	}
	public synchronized void out()
	{   
		
		while(flag==false)// t11 t21
		{
			try {this.wait();} catch (InterruptedException e) {	}
		}
		
		System.out.println(Thread.currentThread().getName()+"。。。。消费.."+this.name);
	       flag=false;
	       notifyAll();		
	}
	
}    

主要的矛盾是:在对方线程全部冻结的时候,己方线程唤醒己方线程,我们只要解决这个矛盾,问题就可以解决。而在object类中有一个notifyAll()方法来唤醒所有线程。问题解决。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值