消费者/生产者异常处理

时间:2016-12-31 13:34:12

标签: java concurrency

我有一个循环缓冲区(数组/先出先出),消费者和生产者。生产者将随机数放入数组中,消费者获取第一个数字并检查它是否是相对素数。

我的代码有效,我认为它可以正常工作,但我想改进它。 我不太确定我的“无效运行”方法。我是否应该在其他地方进行异常处理?改变“无限循环”?不应更改方法签名(它们是预定义的)。

我很乐意改进代码的每个建议。 (不关心可见性(公共,......)和静态事物,我只是把它放在一个文件中。

import java.math.BigInteger;
import java.util.Random;

public class ConsProd {

    static class CircularBuffer {

        private BigInteger[] buffer;
        //"pointers"
        private int read;
        private int write;

        public CircularBuffer(int size) {
            this.buffer = new BigInteger[size];
            this.read = 0;
            this.write = 0;
        }

        public boolean isFull() {
            for(int i = 0; i < buffer.length; i++) {
                if(buffer[i] == null)
                    return false;
            }
            return true;
        }

        public boolean isEmpty() {
            for(int i = 0; i < buffer.length; i++) {
                if(buffer[i] != null)
                    return false;
            }
            return true;    
        }

        public synchronized void put(BigInteger element) throws InterruptedException {
            while(isFull()){
                wait();
            }
            buffer[write] = element;
            write = (write+1)%buffer.length;
            notifyAll();
        }

        public synchronized BigInteger take() throws InterruptedException {
            while(isEmpty()){
                wait();
            }
            BigInteger temp = buffer[read];
            buffer[read] = null;
            read = (read+1)%buffer.length;
            notifyAll();
            return temp;
        }   
    }

    static class Consumer implements Runnable {

        private int id;
        private CircularBuffer buffer;

        public Consumer(int id, CircularBuffer b) {
            this.id = id;
            this.buffer = b;
        }

        private void consume(BigInteger e) {
            synchronized(e){
                System.out.println("consumer " + id + " retrieved: " + e);
                if (e.isProbablePrime(100)) {
                    System.out.println("     -----> probably prime!");
                }
            }
        }

        @Override
        public void run() {
            try { // TODO is this the right place to handle the exception? 
                while (!Thread.currentThread().isInterrupted()) {
                    BigInteger e = buffer.take();
                    consume(e);
                }
            } catch (InterruptedException e) { }
        }

    }

    static class Producer implements Runnable {

        private int id;
        private CircularBuffer buffer;

        public Producer(int id, CircularBuffer b) {
            this.id = id;
            this.buffer = b;
        }

        protected BigInteger produce() {
            BigInteger x = new BigInteger(10, new Random());
            System.out.println("producer " + id + " produced:  " + x.toString());
            return x;
        }

        @Override
        public void run() {

            try { // TODO is this the right place to handle the exception? 
                while (!Thread.currentThread().isInterrupted()) {
                    BigInteger e = produce();
                    buffer.put(e);
                }
            } catch (InterruptedException e) { }
        }
    }


    public static void main(String[] args) {

        CircularBuffer cbuf = new CircularBuffer(4);

        Thread t1 = new Thread(new Consumer(1, cbuf));
        Thread t2 = new Thread(new Consumer(2, cbuf));
        Thread t3 = new Thread(new Consumer(3, cbuf));

        Thread t4 = new Thread(new Producer(1, cbuf));
        Thread t5 = new Thread(new Producer(2, cbuf));

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

        t4.start();
        t5.start();
    }
}

1 个答案:

答案 0 :(得分:0)

这是旧的,但我想我会喜欢的。将来,codereview.stackexchange.com的问题似乎更好。

  

对于改进代码的每条建议,我都会感到高兴。

我的第一个回答是,您应该使用ExecutorService而不是Consumer线程和循环。生产者可以保留您编写的内容,尽管我怀疑拥有多线程功能会对您有所帮助。您的e.isProbablePrime()方法比new BigInteger(10, new Random());慢许多数量级。

由于生产者和消费者之间的速度差异,您必须使用有限的ExecutorService创建BlockingQueue。类似于以下内容:

threadPool = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,
                       new LinkedBlockingQueue<Runnable>(100));

然后,当您的制作人创建BigInteger时,它将执行以下操作:

threadPool.put(new Consumer(randomBigInteger());

您的消费者将根本没有循环。这将负责线程,共享缓冲区和所有同步代码。您无需进行管理。尽管您还可以提交Callable<?>并使用Future<?>来获取结果,但是您将需要自己恢复所有结果。

如果您不能使用ExecutorService的东西,那么尽管我会使用LinkedBlockingQueue而不是您的CircularBuffer,但是您的代码看起来还不错。