0

I wrote a test example of using SynchronousQueue in producer-consumer model. But it doesn't work well. Below are my codes:

public class QueueTest {

    String input;
    int pos;
    BlockingQueue<String> queue;
    volatile boolean exitFlag;

    QueueTest()
    {
        for(int i=0; i<10000; i++)
            input += "abcde";
        input += "X";
        pos = 0;
        queue = new SynchronousQueue<String>();
        exitFlag = false;
    }

    public static void main(String[] args) {
        QueueTest qtest = new QueueTest();
        qtest.runTest();
    }

    void runTest()
    {
        Thread producer = new Thread( new Producer());
        Thread consumer = new Thread( new Consumer());
        producer.start();
        consumer.start();
        try {
            producer.join();
            consumer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    class Producer implements Runnable
    {
        public void run()
        {
            while(true)
            {
                String s = read();
                if(s.equals("X"))
                    break;
                try {
                    queue.put(s);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            exitFlag = true;
        }
    }

    class Consumer implements Runnable
    {
        public void run()
        {
            while(exitFlag == false)
            {
                String s = null;
                try {
                    s = queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                process(s);  
            }
        }
    }

    String read()
    {
        String str = input.substring(pos, pos+1);
        pos++;
        return str;
    }

    void process(String s)
    {
        long sum = 0;
        for(long i=0; i<1000; i++)
            sum = sum * i + i;
    }
}

The problem is the running is stuck like a deadlock. Is there any bugs in these simple codes?

JackWM
  • 10,085
  • 22
  • 65
  • 92

3 Answers3

1

You are more then likely seeing a race condition. Imagine the scenario

Thread 1 put into queue
Thread 2 takes out of queue quickly processes and awaits another put from thread 1
Thread 1 finishes and sets exitFlag to true

In this case Thread 2 will sit the permanently since exitFlag was not set to false before Thread 2 read from it.

You may want to consider a poison pill. It's a message to the other thread that we have completed. For instance:

   final String POISON_PILL = " POISON";

class Producer implements Runnable {
    public void run() {
        while (true) {
            String s = read();
            if (s.equals("X"))
                break;
            try {
                queue.put(s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            queue.put(POISON_PILL);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class Consumer implements Runnable {
    public void run() {
        String s = null;
        try {
            while ((s = queue.take()) != POISON_PILL) {
                process(s);
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

So when the other thread is notified the other thread has completed both threads should end gracefully.

John Vint
  • 39,695
  • 7
  • 78
  • 108
0

Since your exitFlag is shared between multiple threads, you must do something to make the update by the Producer visible to the Consumer (in terms of the java memory model). for this example, making the value volatile would be sufficient.

UPDATE:

You should generate a stack dump of your hung code. That will give you a clue as to what is happening. This code is a good example as to why you shouldn't use a flag for control along with a BlockingQueue.

UPDATE 2:

Bummer, @JohnVint let the cat out of the bag. Yes, the poison pill is the solution to this race condition.

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
0

Your program will be stuck in the following scenario:

Producer sets the exitFlag (without putting a new element) just after the Consumer checks whether existFlag is true. If there is no more elements in the queue (Consumer managed to process all the elements before) consumer will be blocked on queue.take().

You can use queue.poll(), which is no blocking method. It would require changing your program a little bit.

Skarab
  • 6,981
  • 13
  • 48
  • 86