0

I'm working on a producer/consumer pattern, my producer should wait the answer that can be a result object or an exception. What is the best way to do so? I read many examples of this pattern but every time the producer never minds about a return value or of a consumer exception.

public class BlockingQueueExample {

    public static void main(String[] args) throws Exception {

        BlockingQueue queue = new ArrayBlockingQueue(1024);
        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);
        new Thread(producer).start();
        new Thread(consumer).start();
        Thread.sleep(4000);
    }
}

public class Producer implements Runnable{

    protected BlockingQueue queue = null;

    public Producer(BlockingQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            queue.put(new Job("test"));
            //here I need the job result or exception
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Consumer implements Runnable{

    protected BlockingQueue queue = null;

    public Consumer(BlockingQueue queue) {
        this.queue = queue;
    }

    public void run() {
        while(true){
            queue.take();
            try{
                // ... my job
                Result obj;
                // I have to return to producer the result!
            }catch(Exception e){
                //In case of exception I have to return the exception to the producer.
            }
        }
    }
}

MIND: there can be more than one Producer and Consumer.

My only option now is to use a Queue with this object:

public class Job{

    private String input;
    private Exception exception;
    private String result;

    public Jon(String input){
        this.input=input;
        this.exception=null;
    }

    public void setException(Exception exception){
        this.exception=exception;
    }

    public Exception getException(){
        return this.exception;
    }

    public void setResult(String result){
        this.result=result;
    }

    public Exception getResult(){
        return this.result;
    }
}

and let the procuder read back its result or the exception. Otherwise should I use a BlockingQueue of FutureTask?

Tobia
  • 9,165
  • 28
  • 114
  • 219
  • I don't understand the question. What you do now seems fine. You probably should exit the `Producer` on exception. What else did you have in mind? – markspace Dec 06 '15 at 18:45
  • Looking at your problem more, I don't think you have a producer/consumer problem here. If you want to return a result to the producer, that's not actually part of the pattern. You'll need to improvise something. Look at Swing's SwingWorker. https://docs.oracle.com/javase/8/docs/api/javax/swing/SwingWorker.html – markspace Dec 06 '15 at 18:54
  • I try to explain to you the real case. I have n smartcard's thread (one for smartcard) and they have a while loop waiting some jobs. Producers should send their job to the first available smartcard thread. I thought that a BlockingQueue would have been ok. But obviously the Producers needs the result of the job or the exception. – Tobia Dec 06 '15 at 19:56
  • I believe you're thinking about this incorrectly. A Producer should not expect to get results back. Let the Consumer do that. Or a different Consumer, if the design doesn't allow for the former. The whole point of Producer/Consumer is to divide the work, not to have the Producer try to process results. I think you should add more detail *why* you think this smart card reader needs multiple threads. What kind of system is it, and why does threading make sense here? – markspace Dec 06 '15 at 20:39
  • This is a web server, smartcard jobs could arrive from many clients and the smartcards could be more then one, in this case I want to ensure that the client gets the first available smartcard for its job. I don't know if this is producer/consumer patter. But otherwise what is this? – Tobia Dec 06 '15 at 21:22
  • So if it's a web server, then you get a `request` and produce a `result`? Like `HttpServlet` in a JEE application? Why do you want to add threads, the container should be doing that. – markspace Dec 06 '15 at 23:53
  • Http part is managed by a j2ee container and here I'm using the standard solutions. Once the requests arrive to my controller I have to compute a smartcard job. I have a variable pool of smartcards (this can be increased or decreased runtime, inserting a new card or removing) and I have to assign the first available smartcard to the controller. If the job queue is long, controllers will wait a few, in case of empty queue, smartcards will wait for a job. This is a problem very similar to a j2ee thread, db connectio pool, etc. And I thought to use a BlockingQueue... Surely I need a FIFO queue. – Tobia Dec 07 '15 at 08:30
  • I'm spending a lot of time reading many articles about concurrency, but I did not find any solution. Maybe it is better to close and reopen with a more focused question. – Tobia Dec 07 '15 at 16:02
  • There's no point to your idea. If there are not smartcard readers available, what does spawning a thread get you? The original thread has to wait for the response, so it must block too. All you have now is two threads blocked instead of one. Just use the original thread. – markspace Dec 07 '15 at 16:26
  • If there are no smartcard available I should throw an Exception. My idea is to use threads because every thread has its own smartcard, if I have 3 smartcards, I have 3 threads, than I can compute 3 jobs at the same time in case of many requests. Every request get the first free thread and then this thread know its smartcard and start the job. Otherwise what can I do? I cannot use the same thread as the http request because how to choose the free smartcard from smartcards pool? – Tobia Dec 07 '15 at 16:33

2 Answers2

0

First of all, do yourself a favour and start using generics. That should not be a BlockingQueue, it should be a BlockingQueue<Job>. This. Is. 2015.

Secondly, you need two queues. One queue for making inputs available to your consumers, and another queue for collecting outputs from them. So, you can use a BlockingQueue<String> for the input queue, and you can rename Job to Result and have a BlockingQueue<Result> for your output queue.

Of course, while your producer is blocked waiting to read a result from the input queue, you cannot be adding stuff to the output queue, so you will need a different design for your producer. But this is not any worse than the situation you are in right now, where you are (erroneously) expecting to put a job in a queue and somehow (magically?) receive the result back within the same loop.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • I'm using generics, that was only a sample code. I cannot understand the two queue solutions. The Consumer and Producers threads could be more than one, in this case how to get the right answer? – Tobia Dec 06 '15 at 21:19
  • You block waiting for a `Result` from your input queue, and once you have one, you identify which input it pertains to by examining the `input` member of the `Result`. That's why I did not suggest dropping the `input` field from the `Result` class. – Mike Nakis Dec 06 '15 at 21:25
0

You should decouple job queueing from result obtaining. In order to achieve this you can create one more queue and add results to it from consumer thread. Thus you will need one more consumer that responsible for result handling.

a_gura
  • 400
  • 1
  • 5