一、前言
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();
}
}
(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();
}
}
}
四、总结
笔记较乱,日后修改