java 多线程学习之多生产者多消费者产生的线程安全问题分析与解决:Lock和Condition

来源:互联网 发布:qq群网络原因上传失败 编辑:程序博客网 时间:2024/05/17 21:37
//多生产者多消费者//这是一段会产生错误数据的示例class Resource {    private String name;    int count;    boolean flag = false;    public synchronized void produce(String name) {        if (flag) {            try {                this.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        this.name = name + count;        ++count;        System.out.println(Thread.currentThread().getName()+"----" + name+count + "---"+" produced");        flag = true;        notify();    }    public synchronized void consume() {        if (!flag) {            try {                this.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        System.out.println(Thread.currentThread().getName() + name +count+ "consumed");        flag = false;        notify();    }}class Producer implements Runnable {    Resource r;    public Producer(Resource r) {        this.r = r;    }    @Override    public void run() {        while (true) {            r.produce("烤鸭");        }    }}class Consumer implements Runnable {    Resource r;    public Consumer(Resource r) {        this.r = r;    }    @Override    public void run() {        while(true){            r.consume();        }    }}public class ProducerConsumer {    public static void main(String[] args) {        Resource r=new Resource();        Producer producer=new Producer(r);        Consumer consumer=new Consumer(r);        Thread t0=new Thread(producer);        Thread t1=new Thread(producer);        Thread t2=new Thread(producer);        Thread t3=new Thread(consumer);        Thread t4=new Thread(consumer);        Thread t5=new Thread(consumer);        t0.start();        t1.start();        t2.start();        t3.start();        t4.start();        t5.start();

1.线程安全问题产生的原因:

当有一个生产线程开始执行的同时,有另外的生产线程和消费线程在被wait时,生产线程在run方法末尾随机唤醒一个进程,恰好唤醒了另一个生产线程,导致该生产线程在flag=true的情况下仍然可以生产

解决办法1:

  1. 将flag的if判断变成while循环判断,解决了线程获取执行权后是否应该执行的问题。
  2. 将notify改成notifyAll,如果本方唤醒了本方的线程,没有意义,而且while判断+notify会导致死锁。

但是这种方法开销很大,可能造成很多次无用的判断,降低效率

解决办法2:使用Lock和Condition

JDK1.5 以后将同步和锁封装成了对象,并将操作锁的隐式方法变成了显式的动作。

//显式锁的使用示例Lock  lock =new ReentrantLock();lock.lock();try(){.....需要同步的代码块....}finally{lock.unlock();}

condition接口

子类对象可以由lock的方法获得,一个lock可以有多个condition对象。
1. await();
2. signal();
3. signalAll();

//示例Lock lock=new ReentrantLock();Condition cond=lock.newCondition();

使用lock和condition解决多生产者多消费者问题

package MultiThread;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/** * Created by lenovo on 2017/3/14. */class Item{    public int value;    public Item(int value) {        this.value = value;    }}class Buffer {    Lock lock = new ReentrantLock();    Condition notFull = lock.newCondition();    Condition notEmpty = lock.newCondition();    final int Max = 100;    int putIndex=0;    int takeIndex=0;    int count = 0;    final Item[] items = new Item[Max];    public void produce() {        lock.lock();        try {            while (count == Max) {                try {                    notFull.await();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            Item x=new Item(putIndex);            items[putIndex] = x;            System.out.println(x.value+"has been produced");            if (++putIndex == Max)                putIndex = 0;            ++count;            notEmpty.signal();        } finally {            lock.unlock();        }    }    public Item consume() {        lock.lock();        try {            while (count == 0) {                try {                    notEmpty.await();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            Item x = items[takeIndex];            System.out.println(x.value+"has been consumed");            if (++takeIndex == Max)                takeIndex = 0;            --count;            notFull.signal();            return x;        } finally {            lock.unlock();        }    }}class Producer implements Runnable {    Buffer r;    public Producer(Buffer r) {        this.r = r;    }    @Override    public void run() {        while (true) {            r.produce();        }    }}class Consumer implements Runnable {    Buffer r;    public Consumer(Buffer r) {        this.r = r;    }    @Override    public void run() {        while (true) {            r.consume();        }    }}public class ProducerConsumer {    public static void main(String[] args) {        Buffer r = new Buffer();        Producer producer = new Producer(r);        Consumer consumer = new Consumer(r);        Thread t0 = new Thread(producer);        Thread t1 = new Thread(producer);        Thread t2 = new Thread(producer);        Thread t3 = new Thread(consumer);        Thread t4 = new Thread(consumer);        Thread t5 = new Thread(consumer);        t0.start();        t1.start();        t2.start();        t3.start();        t4.start();        t5.start();    }}

**注意点:
1. lock.lock()和lock.unlcok()包围同步代码块
2.lock.unlock() 需要写在finally中,保证即使同步代码块中发生异常,unlock()仍会被执行。
3. 只需唤醒另一个监视器中的一个进程即可,不必signalAll()。**

0 0
原创粉丝点击