黑马程序员————java线程之间的通信

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

  多线程可以让我们同时共享一个资源,但如果在共享这个资源时需要彼此之间的联系怎么做呢?

经典实例:生产者与消费者。

问题描述,生产者每生产一个消费者就要取走一个,同时进行。

首先java为我们提供了一套等待唤醒机制,让线程与线程之间产生了联系。线程是分五个状态的:创建;运行;阻塞;冻结;消亡。java提供了几个针对状态的方法

wait()方法让线程进入冻结状态,让出cpu,让出锁;

notify()方法唤醒进入冻结状态的线程,notifyAll()是唤醒所有的线程。

针对问题,我们可以对生产者设定一个开关,如果资源为0就允许其生产并在生产完之后将开关关上,不会在有生产者生产并唤醒其他线程也就是消费者线程,当然在这之上同样要用到同步,为了保证第一个生产者进去之时,其他生产者会被拒之门外,对于消费者同理。

public static void main(String[] args) {
		// 
		tongbu i=new tongbu();//创建抽取出来的同步方法对象
		hjw j=new hjw(i);//创建实现Runnable接口的对象
		bb k=new bb(i);
		Thread xc1 =new Thread(j);//创建线程
		Thread xc2 =new Thread(k);
		xc1.setName("生产者");
		xc2.setName("消费者");
		xc1.start();
		xc2.start();
	}

}
class tongbu{//将两个同步方法抽取出来
	private String name;
	private int count=1;
	boolean flag=false;//定义一个bool类型来作用等待唤醒机制
	public synchronized void set(String name){//生产者的同步函数
		if(flag){//已存在就睡眠
			try{
				wait();
			}
				
			catch(Exception e){}
			}
		//否则 运作代码
		this.name=name+"---"+count++;
		System.out.println(Thread.currentThread().getName()+this.name);
		flag=true;//改变bool值
		this.notify();//唤醒线程
	}

	public synchronized void get(){
		if(!flag){//进入休眠
			try{
				wait();
			}
				
			catch(Exception e){}
			}
		//被生产者唤醒
		System.out.println(Thread.currentThread().getName()+"---"+this.name);
		flag=false;
		this.notify();//唤醒线程
	}
}
class hjw implements Runnable{
	private tongbu x ;
	hjw(tongbu x){//传回tongbu对象
		this.x=x;
	}
	public void run(){//重写run方法
		
		while(true){
		x.set("饼干");
		}
	}
}
class bb implements Runnable{
	private tongbu x ;
	bb(tongbu x){//传回tongbu对象
		this.x=x;
	}
	public void run(){//重写run方法
		
		while(true){
		x.get();
		}
	}
}
	

  这只是对于生产者和消费者只有一个线程,如果这边都有多个线程呢?绝不是多创建几个线程那么简单,因为要notify方法是唤醒最先休眠的那个线程,也就是说转到后面会出,生产一个,消费两次,生产多个只消费其中一个。

那么如何避免呢?采用while循环替代if循环,每当线程解冻之后,重新开始循环而非接着向下执行,这样就避免了多次执行,但问题是依据notify()的特性还是会让所有线程都陷入等待,我需要唤醒对方线程,所有这个时候就要用到notifyAll()了,唤醒所有线程。

public static void main(String[] args) {
		// 
		tongbu i=new tongbu();//创建抽取出来的同步方法对象
		hjw j=new hjw(i);//创建实现Runnable接口的对象
		bb k=new bb(i);
		Thread xc1 =new Thread(j);//创建线程
		Thread xc2 =new Thread(k);
		xc1.setName("生产者");
		xc2.setName("消费者");
		xc1.start();
		xc2.start();
	}

}
class tongbu{//将两个同步方法抽取出来
	private String name;
	private int count=1;
	boolean flag=false;//定义一个bool类型来作用等待唤醒机制
	public synchronized void set(String name){//生产者的同步函数
		while(flag){//已存在就睡眠
			try{
				wait();
			}
				
			catch(Exception e){}
			}
		//否则 运作代码
		this.name=name+"---"+count++;
		System.out.println(Thread.currentThread().getName()+this.name);
		flag=true;//改变bool值
		this.notifyAll();//唤醒线程
	}

	public synchronized void get(){
		while(!flag){//进入休眠
			try{
				wait();
			}
				
			catch(Exception e){}
			}
		//被生产者唤醒
		System.out.println(Thread.currentThread().getName()+"---"+this.name);
		flag=false;
		this.notifyAll();//唤醒线程
	}
}
class hjw implements Runnable{
	private tongbu x ;
	hjw(tongbu x){//传回tongbu对象
		this.x=x;
	}
	public void run(){//重写run方法
		
		while(true){
		x.set("饼干");
		}
	}
}
class bb implements Runnable{
	private tongbu x ;
	bb(tongbu x){//传回tongbu对象
		this.x=x;
	}
	public void run(){//重写run方法
		
		while(true){
		x.get();
		}
	}
}

  事实上,java5.0版本提供更完善的解决方案,针对如何唤醒指定线程。在java.lang.util.concurrent.locks中,提供了方法将锁显示化用以取代同步synchronized,lock()开锁,unlock()关锁,关锁这一特点犹如关闭资源就是无论怎样都要执行,因为前面有异常需要处理,所以关锁这一操作放在了finally{}中;而对于Object中wait()方法和notify(),notifyAll()方法分别用condition的await(),signal()和signalAll()取代,在lock中有一个new condition的方法,所以可以通过创建多个condition类,实现对指定线程的操作。

原文地址:https://www.cnblogs.com/huangjiawei/p/4706314.html