Java中的多个消费者生产者

时间:2018-08-29 17:28:02

标签: java multithreading producer-consumer

我试图为消费者创建2个线程,为生产者创建2个线程。 所有4个线程都在争用一种资源。 其中两个试图从资源中消费,两个试图生产。

下面是代码

package com.threading;

import java.util.ArrayList;
import java.util.List;

public class TestConsumerProducer2 {

    protected static int maxSize = 2;

    static class Consumer implements Runnable {
        List<Integer> goods;

        public Consumer(List<Integer> goods) {
            this.goods = goods;
        }

        public void consume() {
            synchronized (goods) {

                if (goods.size() <= 0) {
                    try {
                        goods.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + " >>>> consuming >>>" + goods.remove(0));
                goods.notifyAll();
            }
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                consume();
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    static class Producer implements Runnable {
        List<Integer> goods;

        public Producer(List<Integer> goods) {
            this.goods = goods;
        }

        public void produce(int i) {
            synchronized (goods) {

                if (goods.size() >= maxSize) {
                    try {
                        goods.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + ">>> producing >> " + i);
                goods.add(i);
                goods.notifyAll();
            }
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            for (int i = 0; i < 10; i++) {
                produce(i);
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        List<Integer> goods = new ArrayList<>();
        Consumer consumer = new Consumer(goods);
        Producer producer = new Producer(goods);
        Thread consumerWorker1 = new Thread(consumer);
        Thread consumerWorker2 = new Thread(consumer);

        Thread prroducerWorker1 = new Thread(producer);
        Thread prroducerWorker2 = new Thread(producer);

        consumerWorker1.start();
        consumerWorker2.start();
        prroducerWorker1.start();
        prroducerWorker2.start();

        try {
            consumerWorker1.join();
            consumerWorker2.join();
            prroducerWorker1.join();
            prroducerWorker2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("Job completed >>>>");
    }
}

程序输出

  

线程2 >>>产生>> 0

     

线程1 >>>>正在消耗>>> 0

     

线程3 >>>产生>> 0

     

线程“ Thread-0”中的异常java.lang.IndexOutOfBoundsException:   索引:0,大小:0 at   java.util.ArrayList.rangeCheck(ArrayList.java:657)在   java.util.ArrayList.remove(ArrayList.java:496)在   com.threading.TestConsumerProducer2 $ Consumer.consume(TestConsumerProducer2.java:27)     在   com.threading.TestConsumerProducer2 $ Consumer.run(TestConsumerProducer2.java:35)     在java.lang.Thread.run(Thread.java:748)处Thread-2 >>>产生>> 1

     

线程1 >>>>正在消耗>>> 0

     

线程3 >>>产生>> 1

     

线程1 >>>>消耗>>> 1

     

线程2 >>>产生>> 2

     

线程1 >>>>消耗>>> 1

     

线程3 >>>产生>> 2

     

线程1 >>>>正在消耗>>> 2

     

线程2 >>>产生>> 3

     

线程1 >>>>正在消耗>>> 2

     

线程2 >>>产生>> 4

     

线程3 >>>产生>> 3

     

线程1 >>>>正在消耗>>> 3

     

线程2 >>>产生>> 5

     

线程1 >>>>正在消耗>>> 4

     

线程3 >>>产生>> 4

     

线程1 >>>>消耗>>> 3

     

Thread-2 >>>产生>> 6 Thread-1 >>>>消耗>>> 5

     

线程2 >>>产生>> 7

     

线程3 >>>产生>> 5

问题陈述: 为什么没有一个线程执行10次? 代码中的死锁情况在哪里? 当商品对象被使用者线程锁定并且size <= 0时,为什么应该进入等待状态,为什么会有IndexOutOfBoundsException?

1 个答案:

答案 0 :(得分:3)

将代码中的if更改为while可解决此问题。

Guarded Blocks tutorial on Oracle's site中有相关建议,内容如下:

  

注意:始终在测试等待条件的循环内调用wait。不要以为中断是针对您正在等待的特定条件,还是该条件仍然为真。

(通过中断,它们表示从等待中返回,而不一定是源自调用Thread.interrupt的人的实际中断。)

要点:

  • 仅当线程在检查时拥有锁时,线程才知道商品清单的内容。

  • 调用wait放弃锁,使其他线程在此线程处于休眠状态时可以进行处理。

  • 一旦线程放弃了锁,先前对商品清单状态所做的任何检查将不再有效。

一旦线程从等待中返回,则它已重新获取了锁,但是该线程需要重新评估条件检查,否则它将作用于陈旧的信息。在线程上次检查条件的时间与当前时间之间可能发生了很多事情。之所以会收到IllegalArgumentException,是因为当前线程假定当前线程正在等待时,另一个线程已将其删除。

package com.threading;

import java.util.ArrayList; 

import java.util.List;

public class TestConsumerProducer2 {

protected static int maxSize = 2;

static class Consumer implements Runnable {
    List<Integer> goods;

    public Consumer(List<Integer> goods) {
        this.goods = goods;
    }

    public void consume() {
        synchronized (goods) {

            while (goods.size() <= 0) {
                try {
                    goods.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + " >>>> consuming >>>" + goods.remove(0));
            goods.notifyAll();
        }
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            consume();
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

static class Producer implements Runnable {
    List<Integer> goods;

    public Producer(List<Integer> goods) {
        this.goods = goods;
    }

    public void produce(int i) {
        synchronized (goods) {

            while (goods.size() >= maxSize) {
                try {
                    goods.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ">>> producing >> " + i);
            goods.add(i);
            goods.notifyAll();
        }
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 10; i++) {
            produce(i);
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

public static void main(String[] args) {
    List<Integer> goods = new ArrayList<>();
    Consumer consumer = new Consumer(goods);
    Producer producer = new Producer(goods);
    Thread consumerWorker1 = new Thread(consumer);
    Thread consumerWorker2 = new Thread(consumer);

    Thread prroducerWorker1 = new Thread(producer);
    Thread prroducerWorker2 = new Thread(producer);

    consumerWorker1.start();
    consumerWorker2.start();
    prroducerWorker1.start();
    prroducerWorker2.start();

    try {
        consumerWorker1.join();
        consumerWorker2.join();
        prroducerWorker1.join();
        prroducerWorker2.join();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    System.out.println("Job completed >>>>");
}

}

代码现在成功完成,输出如下:

C:\>java com.threading.TestConsumerProducer2
Thread-2>>> producing >> 0
Thread-1 >>>> consuming >>>0
Thread-3>>> producing >> 0
Thread-0 >>>> consuming >>>0
Thread-2>>> producing >> 1
Thread-3>>> producing >> 1
Thread-0 >>>> consuming >>>1
Thread-1 >>>> consuming >>>1
Thread-2>>> producing >> 2
Thread-3>>> producing >> 2
Thread-0 >>>> consuming >>>2
Thread-1 >>>> consuming >>>2
Thread-2>>> producing >> 3
Thread-0 >>>> consuming >>>3
Thread-3>>> producing >> 3
Thread-1 >>>> consuming >>>3
Thread-2>>> producing >> 4
Thread-0 >>>> consuming >>>4
Thread-3>>> producing >> 4
Thread-1 >>>> consuming >>>4
Thread-2>>> producing >> 5
Thread-0 >>>> consuming >>>5
Thread-3>>> producing >> 5
Thread-1 >>>> consuming >>>5
Thread-2>>> producing >> 6
Thread-0 >>>> consuming >>>6
Thread-3>>> producing >> 6
Thread-1 >>>> consuming >>>6
Thread-2>>> producing >> 7
Thread-0 >>>> consuming >>>7
Thread-3>>> producing >> 7
Thread-1 >>>> consuming >>>7
Thread-2>>> producing >> 8
Thread-0 >>>> consuming >>>8
Thread-3>>> producing >> 8
Thread-1 >>>> consuming >>>8
Thread-2>>> producing >> 9
Thread-0 >>>> consuming >>>9
Thread-3>>> producing >> 9
Thread-1 >>>> consuming >>>9
Job completed >>>>