多线程学习笔记(三)---- 生产者消费者问题

一、前言

1、介绍

本篇内容总结自己看视频的笔记,其中“生产者消费者”问题是单个生产者和单个消费者,至于多生产者多消费者问题,以后会再写。

2、两个池的概念

  • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中;
  • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中;

所谓唤醒线程,可以说是将线程由等待池移动到锁池。

二、JDK1.5之前的解决办法

1、代码实现

//资源对象
public class Resource {
	private String name;
	private int count = 1;
	private boolean flag = false;
	public synchronized void set(String name)
	{
		while(flag)
			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)
			try{this.wait();}catch(InterruptedException e){}	
		System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);
		flag = false;
		notifyAll();
	}
}
//生产者对象
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鸭");
		}
	}
}
//消费者对象
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}
//测试
public class ProducerConsumerDemo {
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);
		Thread t0 = new Thread(pro);
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(con);
		t0.start();
		t1.start();
		t2.start();
		t3.start();
	}
}

2、注意

  • if改成while:为了每次线程醒来,都去判断一次条件,这样更安全;
  • notify改成notifyAll:视情况而定,看你是否能不能将对方唤醒;

三、JDK1.5后提供的办法

1、介绍

在JDK1.5之前,使用notifyAll()方法,完全是被迫无奈。因为在JDK1.5之前,没有提供唤醒特定线程的方法。
在我们使用notifyAll()的场景,其实真正的需求是唤醒对方线程。例如,在生产方法的notifyAll()时,是为了唤醒消费者进程来取产品。但是,JDK1.5之前,没有特定的方法可以唤醒对方线程,所以我们只能唤醒线程池里的所有的线程。
在JDK1.5后,已经解决该问题,我们可以直接唤醒对方线程。

2、相关知识

(1)Lock接口

  • 作用:替换了同步代码块和同步函数,将同步的隐式锁操作变成显示锁操作;可以为一个锁加多组监视器;
  • 举例
public class Resource {
	private String name;
	private int count = 1;
	private boolean flag = false;
	
	private Lock lock = new ReentrantLock();//创建同步对象
	public void set(String name)
	{
		lock.lock();//获取锁
		try{
			while(flag)
				try{this.wait();}catch(InterruptedException e){}
			
			this.name = name + count;
			count++;
			System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);//生产烤鸭1 生产烤鸭2 生产烤鸭3
			flag = true;
			notifyAll();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();//释放锁
		}
	}
	public synchronized void out()
	{
		while(!flag)
			try{this.wait();}catch(InterruptedException e){}	
		System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
		flag = false;
		notifyAll();
	}
}
  • 分析
    多线程学习笔记(三)---- 生产者消费者问题_第1张图片
    将synchronized的功能合成一个对象,Lock。
    lock.lock();//获取锁
    lock.unlock();//释放锁,通常放在finally代码块中

(2)Condition接口

  • 作用:代替了Object的wait、notify、notifyAll方法,将这些监视器方法单独进行封装,变成Condition监视器对象。
  • 如何获取
    Condition con = lock.newCondition();
  • 相关方法
    void await():相等于wait();
    void signal():相当于notify();
    void signalAll():相当于notifyAll();
  • 举例
public class Resource {
	private String name;
	private int count = 1;
	private boolean flag = false;
	//创建锁对象
	private Lock lock = new ReentrantLock();
	//为锁对象添加一个监听器对象
	Condition con = lock.newCondition();
	
	public void set(String name)
	{
		lock.lock();//获取锁
		try{
			while(flag)
				try{con.wait();}catch(InterruptedException e){}
			
			this.name = name + count;
			count++;
			System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);//生产烤鸭1 生产烤鸭2 生产烤鸭3
			flag = true;
			//notifyAll();
			con.signalAll();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();//释放锁
		}
	}
	public synchronized void out()
	{
		while(!flag)
			try{this.wait();}catch(InterruptedException e){}	
		System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
		flag = false;
		notifyAll();
	}
}

3、代码实现

public class Resource {
	private String name;
	private int count = 1;
	private boolean flag = false;
	//创建锁对象
	private Lock lock = new ReentrantLock();
	//为锁创建俩个监听器,for生产者和消费者
	Condition consuomer_con = lock.newCondition();
	Condition produce_con = lock.newCondition();
	
	
	public void set(String name)
	{
		lock.lock();//获取锁
		try{
			while(flag)
				try{produce_con.wait();}catch(InterruptedException e){}
			
			this.name = name + count;
			count++;
			System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);//生产烤鸭1 生产烤鸭2 生产烤鸭3
			flag = true;
			//notifyAll();
			//con.signalAll();
			consuomer_con.signal();//直接只唤醒消费者线程,不需要唤醒所有线程!!!!唤醒一个消费者线程
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();//释放锁
		}
	}
	public void out()
	{
		lock.lock();
		try{
			while(!flag)
				try{consuomer_con.wait();}catch(InterruptedException e){}	
			System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
			flag = false;
			//notifyAll();
			produce_con.signal();
		}finally{
			lock.unlock();
		}
		
	}
}

四、总结

笔记较乱,日后修改

你可能感兴趣的:(多线程学习笔记(三)---- 生产者消费者问题)