Producer/Consumer threads using a Queue

前端 未结 7 1704
抹茶落季
抹茶落季 2020-11-22 17:00

I\'d like to create some sort of Producer/Consumer threading app. But I\'m not sure what the best way to implement a queue between the two.

So I\'ve so

相关标签:
7条回答
  • 2020-11-22 17:38
    1. Java code "BlockingQueue" which has synchronized put and get method.
    2. Java code "Producer" , producer thread to produce data.
    3. Java code "Consumer" , consumer thread to consume the data produced.
    4. Java code "ProducerConsumer_Main", main function to start the producer and consumer thread.

    BlockingQueue.java

    public class BlockingQueue 
    {
        int item;
        boolean available = false;
    
        public synchronized void put(int value) 
        {
            while (available == true)
            {
                try 
                {
                    wait();
                } catch (InterruptedException e) { 
                } 
            }
    
            item = value;
            available = true;
            notifyAll();
        }
    
        public synchronized int get()
        {
            while(available == false)
            {
                try
                {
                    wait();
                }
                catch(InterruptedException e){
                }
            }
    
            available = false;
            notifyAll();
            return item;
        }
    }
    

    Consumer.java

    package com.sukanya.producer_Consumer;
    
    public class Consumer extends Thread
    {
        blockingQueue queue;
        private int number;
        Consumer(BlockingQueue queue,int number)
        {
            this.queue = queue;
            this.number = number;
        }
    
        public void run()
        {
            int value = 0;
    
            for (int i = 0; i < 10; i++) 
            {
                value = queue.get();
                System.out.println("Consumer #" + this.number+ " got: " + value);
            }
        }
    }
    

    ProducerConsumer_Main.java

    package com.sukanya.producer_Consumer;
    
    public class ProducerConsumer_Main 
    {
        public static void main(String args[])
        {
            BlockingQueue queue = new BlockingQueue();
            Producer producer1 = new Producer(queue,1);
            Consumer consumer1 = new Consumer(queue,1);
            producer1.start();
            consumer1.start();
        }
    }
    
    0 讨论(0)
  • 2020-11-22 17:38

    Use this typesafe pattern with poison pills:

    public sealed interface BaseMessage {
    
        final class ValidMessage<T> implements BaseMessage {
    
            @Nonnull
            private final T value;
    
    
            public ValidMessage(@Nonnull T value) {
                this.value = value;
            }
    
            @Nonnull
            public T getValue() {
                return value;
            }
    
            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                ValidMessage<?> that = (ValidMessage<?>) o;
                return value.equals(that.value);
            }
    
            @Override
            public int hashCode() {
                return Objects.hash(value);
            }
    
            @Override
            public String toString() {
                return "ValidMessage{value=%s}".formatted(value);
            }
        }
    
        final class PoisonedMessage implements BaseMessage {
    
            public static final PoisonedMessage INSTANCE = new PoisonedMessage();
    
    
            private PoisonedMessage() {
            }
    
            @Override
            public String toString() {
                return "PoisonedMessage{}";
            }
        }
    }
    
    public class Producer implements Callable<Void> {
    
        @Nonnull
        private final BlockingQueue<BaseMessage> messages;
    
        Producer(@Nonnull BlockingQueue<BaseMessage> messages) {
            this.messages = messages;
        }
    
        @Override
        public Void call() throws Exception {
            messages.put(new BaseMessage.ValidMessage<>(1));
            messages.put(new BaseMessage.ValidMessage<>(2));
            messages.put(new BaseMessage.ValidMessage<>(3));
            messages.put(BaseMessage.PoisonedMessage.INSTANCE);
            return null;
        }
    }
    
    public class Consumer implements Callable<Void> {
    
        @Nonnull
        private final BlockingQueue<BaseMessage> messages;
    
        private final int maxPoisons;
    
    
        public Consumer(@Nonnull BlockingQueue<BaseMessage> messages, int maxPoisons) {
            this.messages = messages;
            this.maxPoisons = maxPoisons;
        }
    
        @Override
        public Void call() throws Exception {
            int poisonsReceived = 0;
            while (poisonsReceived < maxPoisons && !Thread.currentThread().isInterrupted()) {
                BaseMessage message = messages.take();
                if (message instanceof BaseMessage.ValidMessage<?> vm) {
                    Integer value = (Integer) vm.getValue();
                    System.out.println(value);
                } else if (message instanceof BaseMessage.PoisonedMessage) {
                    ++poisonsReceived;
                } else {
                    throw new IllegalArgumentException("Invalid BaseMessage type: " + message);
                }
            }
            return null;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 17:41

    OK, as others note, the best thing to do is to use java.util.concurrent package. I highly recommend "Java Concurrency in Practice". It's a great book that covers almost everything you need to know.

    As for your particular implementation, as I noted in the comments, don't start Threads from Constructors -- it can be unsafe.

    Leaving that aside, the second implementation seem better. You don't want to put queues in static fields. You are probably just loosing flexibility for nothing.

    If you want to go ahead with your own implementation (for learning purpose I guess?), supply a start() method at least. You should construct the object (you can instantiate the Thread object), and then call start() to start the thread.

    Edit: ExecutorService have their own queue so this can be confusing.. Here's something to get you started.

    public class Main {
        public static void main(String[] args) {
            //The numbers are just silly tune parameters. Refer to the API.
            //The important thing is, we are passing a bounded queue.
            ExecutorService consumer = new ThreadPoolExecutor(1,4,30,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(100));
    
            //No need to bound the queue for this executor.
            //Use utility method instead of the complicated Constructor.
            ExecutorService producer = Executors.newSingleThreadExecutor();
    
            Runnable produce = new Produce(consumer);
            producer.submit(produce);   
        }
    }
    
    class Produce implements Runnable {
        private final ExecutorService consumer;
    
        public Produce(ExecutorService consumer) {
            this.consumer = consumer;
        }
    
        @Override
        public void run() {
            Pancake cake = Pan.cook();
            Runnable consume = new Consume(cake);
            consumer.submit(consume);
        }
    }
    
    class Consume implements Runnable {
        private final Pancake cake;
    
        public Consume(Pancake cake){
            this.cake = cake;
        }
    
        @Override
        public void run() {
            cake.eat();
        }
    }
    

    Further EDIT: For producer, instead of while(true), you can do something like:

    @Override
    public void run(){
        while(!Thread.currentThread().isInterrupted()){
            //do stuff
        }
    }
    

    This way you can shutdown the executor by calling .shutdownNow(). If you'd use while(true), it won't shutdown.

    Also note that the Producer is still vulnerable to RuntimeExceptions (i.e. one RuntimeException will halt the processing)

    0 讨论(0)
  • 2020-11-22 17:41

    You are reinventing the wheel.

    If you need persistence and other enterprise features use JMS (I'd suggest ActiveMq).

    If you need fast in-memory queues use one of the impementations of java's Queue.

    If you need to support java 1.4 or earlier, use Doug Lea's excellent concurrent package.

    0 讨论(0)
  • 2020-11-22 17:55

    Java 5+ has all the tools you need for this kind of thing. You will want to:

    1. Put all your Producers in one ExecutorService;
    2. Put all your Consumers in another ExecutorService;
    3. If necessary, communicate between the two using a BlockingQueue.

    I say "if necessary" for (3) because from my experience it's an unnecessary step. All you do is submit new tasks to the consumer executor service. So:

    final ExecutorService producers = Executors.newFixedThreadPool(100);
    final ExecutorService consumers = Executors.newFixedThreadPool(100);
    while (/* has more work */) {
      producers.submit(...);
    }
    producers.shutdown();
    producers.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    consumers.shutdown();
    consumers.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    

    So the producers submit directly to consumers.

    0 讨论(0)
  • 2020-11-22 18:03

    This is a very simple code.

    import java.util.*;
    
    // @author : rootTraveller, June 2017
    
    class ProducerConsumer {
        public static void main(String[] args) throws Exception {
            Queue<Integer> queue = new LinkedList<>();
            Integer buffer = new Integer(10);  //Important buffer or queue size, change as per need.
    
            Producer producerThread = new Producer(queue, buffer, "PRODUCER");
            Consumer consumerThread = new Consumer(queue, buffer, "CONSUMER");
    
            producerThread.start();  
            consumerThread.start();
        }   
    }
    
    class Producer extends Thread {
        private Queue<Integer> queue;
        private int queueSize ;
    
        public Producer (Queue<Integer> queueIn, int queueSizeIn, String ThreadName){
            super(ThreadName);
            this.queue = queueIn;
            this.queueSize = queueSizeIn;
        }
    
        public void run() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == queueSize){
                        System.out.println(Thread.currentThread().getName() + " FULL         : waiting...\n");
                        try{
                            queue.wait();   //Important
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
    
                    //queue empty then produce one, add and notify  
                    int randomInt = new Random().nextInt(); 
                    System.out.println(Thread.currentThread().getName() + " producing... : " + randomInt); 
                    queue.add(randomInt); 
                    queue.notifyAll();  //Important
                } //synchronized ends here : NOTE
            }
        }
    }
    
    class Consumer extends Thread {
        private Queue<Integer> queue;
        private int queueSize;
    
        public Consumer(Queue<Integer> queueIn, int queueSizeIn, String ThreadName){
            super (ThreadName);
            this.queue = queueIn;
            this.queueSize = queueSizeIn;
        }
    
        public void run() {
            while(true){
                synchronized (queue) {
                    while(queue.isEmpty()){
                        System.out.println(Thread.currentThread().getName() + " Empty        : waiting...\n");
                        try {
                            queue.wait();  //Important
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
    
                    //queue not empty then consume one and notify
                    System.out.println(Thread.currentThread().getName() + " consuming... : " + queue.remove());
                    queue.notifyAll();
                } //synchronized ends here : NOTE
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题