您的位置:首页 > 编程语言 > Java开发

JAVA--多线程生产者消费者问题详解

2016-07-18 13:48 465 查看
最近学到多线程了,之前在网上看了很多博客,解释的都不怎么详细,对于新手来说确实比较难懂,所以我就来分享一下自己的理解,希望能够帮助到需要的人。

首先,来解释一下线程间的通信:就是多个线程执行的任务不同,但是操作的数据相同。我们需要线程同步的执行,这时要用到同一把锁,锁可以是任意的对象,这就是wait(),notify(),notifyAll()方法在Object里而不是在Thread里的原因了,这些方法必须用在同步中,因为同步中才有锁。在这里,资源类的对象就可以作为锁。不多说,先看看单个生产者和单个消费者的代码。

一.单个生产者和单个消费者

class Product
{
private String name;
private int count;
private boolean flag; //标志
//生产功能
public synchronized void product(String name){
if(flag)
try{this.wait();}catch(InterruptedException e){e.printStackTrace();}
this.name = name+"...."+count;
System.out.println(Thread.currentThread().getName()+"生产了"+this.name);
count++;
flag = true;
this.notify();
}
//消费功能
public synchronized void consume(){
if(!flag)
try{this.wait();}catch(InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"消费了"+name);
flag = false;
this.notify();
}
}
//生产者类
class Productor implements Runnable
{
private Product product;
Productor(Product product){
this.product = product;
}
public void run(){
while(true){
product.product("笔记本电脑");
}
}

}
//消费者类
class Consumer implements Runnable
{
private Product product;
Consumer(Product product){
this.product = product;
}
public void run(){
while(true){
product.consume();
}
}
}
class Pro_Con
{
public static void main(String[] args)
{
Product product = new Product();
Productor productor = new Productor(product);
Consumer consumer = new Consumer(product);
new Thread(productor).start();//一个生产线程
new Thread(consumer).start();//一个消费线程
}
}


this.wait() 指明持有哪个锁的线程去等待,让线程等待(会放弃锁),把线程放入了线程池.

this.notify() 唤醒线程池中的任意一个线程

这里用到的是synchronized来进行同步,当标志位为true时,等待并释放锁;当标志位为false时生产者生产一个,标志位置为true,唤醒消费者线程。

二.多个生产者和多个消费者(PS:生产单个,消费单个)

当有多个生产者和消费者时,我们上面的代码就不安全了,会发生生产者生产了两个,消费者消费一个或生产一个,消费两个的情况。我来简单分析下:

生产者1:抢到CPU资源,判断flag为false,生产一个,标志位置为true,空唤醒,再判断,为true,进入等待状态

生产者2:抢到CPU资源,进入,发现flag已经被设为true了,进入等待状态。

消费者1:抢到CPU资源,判断flag为true,消费一个,标记为置flase,如果唤醒的是生产者1。生产者一判断为false,生产一个,标记位置为true。

这时,如果唤醒的如果是生产者2,生产者2不会再判断,直接执行下面的代码,这时就会出现生产两个,消费一个的情况。

那该怎么解决呢???

要让它重新判断,把判断语句if(),改为while()就行了 ,但是会产生死锁,原因是当唤醒的是本方线程时会导致所有线程都等待。因为notify唤醒的是任意一个线程,不能保证唤醒的是对方线程,所以使用notifyAll()唤醒所有线程。

//生产功能
public synchronized void product(String name){
while(flag)//此处
try{this.wait();}catch(InterruptedException e){e.printStackTrace();}
this.name = name+"...."+count;
System.out.println(Thread.currentThread().getName()+"生产了"+this.name);
count++;
flag = true;
this.notifyAll();//此处
}

//消费功能
public synchronized void consume(){
while(!flag) //此处
try{this.wait();}catch(InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"消费了"+name);
flag = false;
this.notifyAll();//此处
}


三.使用Lock和Condition接口替代synchronized(优化程序性能)

上一个例子使用了notifyAll()方法解决了死锁的问题,但是每次都唤醒所有的线程会使程序的性能降低。JDK1.5之后对锁进行了单独描述,使用的接口Lock替代synchronized的方式:

·创建一把锁,也就是创建Lokc的子类对象

·把需要同步的代码放在lock()和unlock()之间

·Lock lock = new ReentrantLock();

Condition condition = lock.newCondition()

获取和锁绑定的condition对象

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Product
{
private String name;
private int count;
private boolean flag;
//创建一个锁对象
private Lock lock = new ReentrantLock();
//获取和锁绑定的生产者condition对象
private Condition pro = lock.newCondition();
//获取和锁绑定的消费者condition对象
private Condition con = lock.newCondition();

//生产功能,注意:synchronized已经被替代了
public void product(String name){
lock.lock(); //获取锁
try{
while(flag)
try{pro.await();}catch(InterruptedException e){e.printStackTrace();}
this.name = name+"...."+count;
System.out.println(Thread.currentThread().getName()+"生产了"+this.name);
count++;
flag = true;
con.signal();
}finally{
lock.unlock();//释放锁,因为要释放锁,所以放在finally中
}
}

//消费功能
public void consume(){
lock.lock();
try{
while(!flag)
try{con.await();}catch(InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"消费了"+name);
flag = false;
pro.signal();
}finally{
lock.unlock();
}
}
}


Condition中的await()等待,signal()唤醒线程池中任意一个,有JVM决定,signalAll()唤醒线程池中所有线程。

注:后两个代码只是部分,其他代码参考第一个。

四.多个生产者和多个消费者(PS:生产多个,消费多个)

//使用 jdk1.5的Lock,Condition实现可以同时生产多个,消费多个的功能
import java.util.concurrent.locks.*;
class Clothes
{
//产品的名称
private String name;
//产品的价格
private double price;
//存储产品的容器
private Clothes[] arr = new Clothes[100];
//定义一把锁
private Lock lock = new ReentrantLock();
//操作生产线程的Condition对象
private Condition pro = lock.newCondition();
//操作消费线程的Condition对象
private Condition con = lock.newCondition();
//生产线程和消费线程使用的下标以及产品数量
private int proindex,conindex,count;

public Clothes(){}
public Clothes(String name,double price)
{
this.name = name;
this.price = price;
}
public String toString()
{
return name+","+price;
}
//生产功能
public void produce()
{
lock.lock();
try
{
//当容器满的时候就不能生产
while(arr.length==count)
{
try{pro.await();}catch(InterruptedException e){e.printStackTrace();}
}
arr[proindex] = new Clothes("衬衫",9.99);
System.out.println(Thread.currentThread().getName()+"生产了..."+arr[proindex]);
//判断下标加1后是否和数组长度相同,相同则置为0
if(++proindex==arr.length)
proindex = 0;
//数量加1
count++;
con.signal();//唤醒一个消费线程
}
finally
{
lock.unlock();
}
}
//消费功能
public void consume()
{
lock.lock();
try
{
//当count为0的时候不能消费
while(count==0)
try{con.await();}catch(InterruptedException e){e.printStackTrace();}
//消费一件产品
Clothes yifu = arr[conindex];
System.out.println(Thread.currentThread().getName()+"消费了........"+yifu);
if(++conindex==arr.length)
conindex = 0;
//数量减1
count--;
pro.signal();

}
finally
{
lock.unlock();
}
}
}
//生产任务
class Producer implements Runnable
{
private  Clothes clo;
public Producer(Clothes clo)
{
this.clo = clo;
}
public void run()
{
while(true)
{
clo.produce();
}
}
}
//消费任务
class Consumer implements Runnable
{
private  Clothes clo;
public Consumer(Clothes clo)
{
this.clo = clo;
}
public void run()
{
while(true)
{
clo.consume();
}
}
}
class Demo7
{
public static void main(String[] args)
{
Clothes clo = new Clothes();

Producer producer = new Producer(clo);
Consumer consumer = new Consumer(clo);

Thread t0 = new Thread(producer);
Thread t1 = new Thread(producer);

Thread t2 = new Thread(consumer);
Thread t3 = new Thread(consumer);

t0.start();
t1.start();
t2.start();
t3.start();

}
}


多个生产者和多个消费者–生产和消费多个与生产和消费单个并没有什么区别,只是加了个容器–数组,将生产的产品存起来,而不是生产一个消费一个。把判断条件改变下就行了,容器满了,生产者不能生产;容器空了,消费者不能消费,仅此而已。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息