我的生产者/消费者多线程程序不起作用

时间:2017-03-26 16:36:41

标签: java multithreading

我努力做一个多线程的java程序,但它大部分时间都不起作用。

在这个程序中有4个线程应该在ArrayList上读写值。特别是2个线程(Productors)应该写入值,2个线程(消费者)应该读取产品所写的值。在使用者读取值之后,应该清除该数组。

有一个类(Buffer)应该管理写/读请求。

我是多线程编程的新手,我无法让它正常工作。 有人能帮我吗?这是代码:

主:

public class App {

    public static void main(String[] args) {
        Buffer m = new Buffer();

        Productor p1 = new Productor("Productor 1", m);
        Productor p2 = new Productor("Productor 2", m);

        Consumer c1 = new Consumer("Consumer 1", m);
        Consumer c2 = new Consumer("Consumer 2", m);

        Thread t1 = new Thread(p1);
        Thread t2 = new Thread(p2);
        Thread t3 = new Thread(c1);
        Thread t4 = new Thread(c2);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

缓冲液:

import java.util.ArrayList;

public class Buffer {
    private ArrayList<Integer> array;
    private boolean state;

    public Buffer() {
        array = new ArrayList<>();
        state = true;
    }

    public synchronized void write(int num) {
        if (!state) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        array.add(num);
        state = false;
        notifyAll();        
    }

    public synchronized void read() {
        if (state) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        System.out.println(array);
        array.clear();
        state = true;
        notifyAll();
    }    
}

生产方:

import java.util.Random;

public class Productor implements Runnable {
    private final String NAME;
    private Buffer market;
    private final Random random = new Random();

    public Productor(String NAME, Buffer market) {
        this.NAME = NAME;
        this.market = market;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            int j = random.nextInt(10);
            market.write(j);
        }
    }
}

最后,消费者:

public class Consumer implements Runnable {
    private final String NAME;
    private Buffer market;

    public Consumer(String NAME, Buffer market) {
        this.NAME = NAME;
        this.market = market;
    }

    @Override
    public void run() {
        for (int i = 0; i < 2; i++) {
            market.read();
        }
    }    
}

感谢。

3 个答案:

答案 0 :(得分:2)

基本问题是:你的两个消费者线程将阅读并清除Buffer总共4次,你的制作人会将Buffer的单个值写入总共20次,如果他们可以。

但他们不能。

在写入完成之前,消费者通常会完成4种读取方式,然后阻止写入,因为state == false。因此,程序通常会在一个或多个生产者被阻止的状态下结束。

但是还有另一个问题。读取和写入都在同一个对象上进行通知,但是如果写入线程唤醒,则无法确定它是否被读取通知,并且因为它没有再次检查状态,它会将更多值写入Buffer 。对于读取,同样如此,通过写入无法确定通知。 您应该始终在检查条件的循环中进行等待,并在条件不成立时再次开始等待。

所以你需要的修复是:

  • state上的if检查更改为while循环
  • 确保消费者做同等数量的阅读。

但是,我想知道除非Buffer为空,否则制作人无法写作。如果生成器被允许写入,即使Buffer具有值,修复程序是:

  • 从写入
  • 中删除state检查和等待指令
  • state上的if检查更改为读取
  • 中的while循环
  • 读取不再需要执行notifyAll。

在这种情况下,读取不确定是否会有更多写入,因为读取操作可能会消耗多个写入值,并且一个或两个消费者线程可能会保持阻塞,因为所有写入在执行第二个循环之前已被占用。为了解决这个问题,我建议你看一下“毒丸”#34;解决方案或确保您的读取只消耗一个值并对齐读写次数。

答案 1 :(得分:0)

您的制作人和消费者计划几乎没有基本问题:

(1)您的消费者运行速度非常快是否等待生产者将元素放在数组

(2)等待和通知的条件不正确

您可以查看以下更新后的代码:

制作类:

public class Productor implements Runnable {
        private final String NAME;
        private Buffer market;
        private final Random random = new Random();

        public Productor(String NAME, Buffer market) {
            this.NAME = NAME;
            this.market = market;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                int j = random.nextInt(10);
                market.write(j);
            }
            market.setCompleted(true);
        }
    }

消费者类:

public class Consumer implements Runnable {
        private final String NAME;
        private Buffer market;

        public Consumer(String NAME, Buffer market) {
            this.NAME = NAME;
            this.market = market;
        }

        @Override
        public void run() {
            market.read();
        }    
    }

缓冲类:

public class Buffer {

        private ArrayList<Integer> array;

        public Buffer() {
            array = new ArrayList<>();
        }

        private volatile boolean completed;

        public boolean isCompleted() {
          return completed;
        }

        public void setCompleted(boolean completed) {
         this.completed = completed;
       }

        public synchronized void write(int num) {
            if (array.size() > 0) {
                try {
                    wait();
                } catch (InterruptedException e) {}
            } else {
                System.out.print(" adding *** ");
                array.add(num);
                notifyAll();     
                System.out.println(num);
            }
        }

        public synchronized void read() {
            while(true) {
                if(isCompleted()) break;
                if (array.size() == 0) {
                    try {
                        wait();
                    } catch (InterruptedException e) {}
                } else {
                    System.out.println(" reading *** "+array);
                    array.clear();
                    notifyAll();
                }
            }
        }    
    }

答案 2 :(得分:0)

  

我已经努力做一个多线程的java程序,但是它的工作次数最多......

是的,多线程程序很难。线程之间的锁定和数据同步并不简单。

听起来你有答案,但将来你应该考虑使用BlockingQueue<Integer>。阻塞队列(例如LinkedBlockingQueue)负责内存同步以及生产者和消费者之间的信令。他们都将共享相同的队列:

final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
Producer p1 = new Productor("Productor 1", queue);
Producer p2 = new Productor("Productor 2", queue);
Consumer c1 = new Consumer("Consumer 1", queue);
Consumer c2 = new Consumer("Consumer 2", queue);

有了它,你的制作人会做类似的事情:

queue.put(num);

并且您的消费者会这样做:

int num = queue.take();

您可以通过为LinkedBlockingQueue构造函数指定数字来限制队列。这样,如果生产者比消费者跑得更快,他们就不会填补记忆。

他们唯一的诀窍就是如何阻止消费者等待物品。在队列中放置特定值有效。完成后,您也可以中断线程。