我根本没有获得线程同步

时间:2012-04-19 21:46:47

标签: java multithreading synchronization

所以我已经阅读了很多关于Java中的线程同步的内容。我正在尝试Bounded-Buffer问题。生产者将继续在缓冲区生产产品,而消费者则继续消费它。

如果缓冲区已满,生产者将等待生产另一个产品。 如果缓冲区为空,消费者将等待。

然而,我的问题是生产者只在缓冲区为空时才开始生产,直到它满了。消费者只有在缓冲区满时才开始消耗,直到它为空。

示例(缓冲区大小:5)

Produced Product 1
Produced Product 2
Produced Product 3
Produced Product 4
Produced Product 5
Consumed Product 1
Consumed Product 2
Consumed Product 3
Consumed Product 4
Consumed Product 5
Produced Product 6
Produced Product 7
Produced Product 8
Produced Product 9
Produced Product 10
Consumed Product 6
Consumed Product 7
Consumed Product 8
Consumed Product 9
Consumed Product 10
Produced Product 11
Produced Product 12
Produced Product 13
Produced Product 14
Produced Product 15
Consumed Product 11
Consumed Product 12
Consumed Product 13
Consumed Product 14
Consumed Product 15
Produced Product 16
Produced Product 17
Produced Product 18
Produced Product 19
Produced Product 20
Consumed Product 16
Consumed Product 17
Consumed Product 18
Consumed Product 19
Consumed Product 20
Produced Product 21
Produced Product 22
Produced Product 23
Produced Product 24
Produced Product 25
Consumed Product 21
Consumed Product 22
Consumed Product 23
Consumed Product 24
Consumed Product 25
Produced Product 26
Produced Product 27
Produced Product 28
Produced Product 29
Produced Product 30
Consumed Product 26
Consumed Product 27
Consumed Product 28
Consumed Product 29
Consumed Product 30

我想要它,这样只要缓冲区未满,生成器就会生成,无论缓冲区是否为空。随后,我想要它,以便只要缓冲区不为空,消费者就会消耗,无论缓冲区是否已满。

我的代码出了什么问题?

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javax.swing.JOptionPane;

public class ProducerConsumer{

    final static Queue<Product> buffer = new LinkedList<>();
    private static int buffer_size, no_items, itemno = 1;
    private static Random r = new Random();
    public static void main(String[] args) {
        try{
            buffer_size = Integer.parseInt(JOptionPane.showInputDialog("Input Buffer size (default: 5)"));
            if(buffer_size<0){
                buffer_size = 5;
            }
        }catch(NumberFormatException nfe){
            buffer_size = 5;
        }
        try{
            no_items = Integer.parseInt(JOptionPane.showInputDialog("Input No. of Items (default: 10)"));
            if(no_items<0){
                no_items = 10;
            }
        }catch(NumberFormatException nfe){
            no_items = 10;
        }
        Producer producer = new Producer();
        Consumer consumer = new Consumer();
        producer.start();
        consumer.start();
    }

    static class Product {
        private String name = "Product X";
        private int productno;

        public Product(int productno) {
            this.productno = productno;
            this.name = "Product "+productno;
        }
        public int number() {
            return productno;
        }
        @Override
        public String toString() {
            return name;
        }
    }
    static class Producer extends Thread{
        public Producer(){
        }
        public void produce(){
            Product p = new Product(itemno++);
            try {
                this.sleep(r.nextInt(100));
            } catch (InterruptedException ex) {
                Thread.interrupted();
            }
            buffer.add(p);
            System.out.println("Produced "+p);
        }
        @Override
        public void run(){
            synchronized(buffer){
                while(itemno<=no_items){
                    while(buffer.size()==buffer_size){
                        try{
                            buffer.wait(100);
                        }catch(InterruptedException e){
                            Thread.interrupted();
                        }
                    }
                    produce();
                    buffer.notifyAll();
                }
            }
        }
    }
    static class Consumer extends Thread{
        public Consumer(){
        }
        public boolean consume(){
            try {
                this.sleep(r.nextInt(100));
            } catch (InterruptedException ex) {
                Thread.interrupted();
            }
            Product product = buffer.remove();
            System.out.println("Consumed "+product);
            return product.number()==no_items;
        }
        @Override
        public void run(){
            synchronized(buffer){
                boolean end = false;
                while(!end){
                    while(buffer.isEmpty()){
                        try{
                            buffer.wait(100);
                        }catch(InterruptedException e){
                            Thread.interrupted();
                        }
                    }
                    end = consume();
                    buffer.notifyAll();
                }
            }
        }
    }
}

2 个答案:

答案 0 :(得分:4)

这是因为你已经获得buffer监视器并在notifyAll之前生成N次并通过退出synchronized块释放缓冲区监视器,所有这些都在while循环中。

尝试将while放在synchronized块之外,这样就可以让另一个线程有机会在并发生成/消耗期间获取锁定。

请注意,notifyAll实际上不影响其他线程,直到通知线程退出synchronized区域

编辑:我在进行建议的更改后运行它,我得到了以下输出

Produced Product 1
Produced Product 2
Produced Product 3
Consumed Product 1
Consumed Product 2
Consumed Product 3
Produced Product 4
Produced Product 5
Consumed Product 4
Consumed Product 5
Produced Product 6
Consumed Product 6
Produced Product 7
Produced Product 8
Produced Product 9
Produced Product 10
Consumed Product 7
Consumed Product 8
Consumed Product 9
Consumed Product 10

答案 1 :(得分:0)

由于两个线程在缓冲区上同步,因此任何时候都只会运行其中一个线程。一个线程通知另一个线程恢复运行并在调用buffer.wait()时挂起自身。 while循环中包含buffer.wait()调用,(buffer.size()== buffer_size)和(buffer.isEmpty())的逻辑允许循环仅在其函数(生产者或消费者)时运行已经完成,无法继续。

程序执行如下:Producer将生成5个项目,然后while循环条件为true,它将调用buffer.wait()以允许使用者运行。消费者将开始运行并消耗,直到缓冲区为空,在这种情况下,while条件为真,它将调用buffer.wait()并允许生产者再次运行。

要允许两个线程同时运行(这可能就是你真正想要的),只有在需要安全访问缓冲区时才应调用synchronize(buffer)。一种方法是使用以下代码块:

static class Producer extends Thread {
    public Producer() {
    }

    public void produce() {
        Product p = new Product(itemno++);
        try {
            this.sleep(r.nextInt(100));
        } catch (InterruptedException ex) {
            Thread.interrupted();
        }
        synchronized(buffer) {
            buffer.add(p);
        }
        System.out.println("Produced " + p);
    }

    @Override
    public void run() {
        while (itemno <= no_items) {
            while (buffer.size() == buffer_size) {
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    Thread.interrupted();
                }
            }

            produce();
        }
    }
}

static class Consumer extends Thread {
    public Consumer() {
    }

    public boolean consume() {
        try {
            this.sleep(r.nextInt(100));
        } catch (InterruptedException ex) {
            Thread.interrupted();
        }

        Product product;
        synchronized (buffer) {
            product = buffer.remove();
        }

        System.out.println("Consumed " + product);
        return product.number() == no_items;
    }

    @Override
    public void run() {
        boolean end = false;
        while (!end) {
            while (buffer.isEmpty()) {
                try {
                    sleep(100);
                } catch (InterruptedException ex) {
                    Logger.getLogger(TrashMe.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            end = consume();
        }
    }
}