0

I am learning java and I am implementing a basic producer-consumer usecase. I have a queue and some Producers that are inserting stuff into this queue. I also have one Consumer that implements Callable. Here is the implementation of Consumer:

public String call() throws Exception
{
    while(true) {
        if(!queue.isEmpty()) {
            String prod = queue.poll();
            System.out.println(name + " consumed " + prod);
        }
    }
}

Producer:

@Override
public void run() {
    System.out.println("adding " + name);
    queue.add(name);
}

Main:

public static void main(String[] args) {
    Test();
}

public static void Test()
{
    Queue<String> queue = new LinkedList<String>();

    ScheduledExecutorService lots_of_producers = Executors.newScheduledThreadPool(10);
    ExecutorService consumers = Executors.newFixedThreadPool(1);


    for(int i=0; i<1; i++) {
        Consumer consumer = new Consumer("consumer_" + i, queue);
        consumers.submit(consumer);
    }

    for(int i=0; i<5; i++) {
        Producer producer = new Producer("producer_" + i, queue);
        lots_of_producers.scheduleWithFixedDelay(producer, i, 5, TimeUnit.SECONDS);
    }
}

Producers work fine but the consumer doesn't work because I didn't see consumer's log.

However, if I change the consume implementation and let the consumer thread sleep when queue.isEmpty() is true, consumer works as expected.

public String call() throws Exception
{
    while(true) {
        if(!queue.isEmpty()) {
            String prod = queue.poll();
            System.out.println(name + " consumed " + prod);
        } else {
            Thread.sleep(100);
        }
    }
}

Can anybody explain why consumer thread is not working in the first case?

Also for the first case, in eclipse I can see that the consumer thread is not dead. I suspend it and it was working on this line

        if(!queue.isEmpty()) {

Now if I manually step over, the consumer will work and print log. But as soon as I resume and let it run by itself, it will get stuck again.

I am trying to understand java more.

Thanks!

-Erben

Erben Mo
  • 3,528
  • 3
  • 19
  • 32
  • 1
    You should use some sort of `BlockingQueue`. – Josh M Jan 26 '14 at 04:31
  • You haven't shown anything about the other side of the code, but I suspect you aren't using an appropriately thread-safe queue implementation. – chrylis -cautiouslyoptimistic- Jan 26 '14 at 04:32
  • Maybe your consumer task died (due to a concurrent modification exception for example), you don't see any exception if that happens since executors return you a future that returns you a result or the exception rather than producing a noisy crash. – zapl Jan 26 '14 at 04:48
  • Creating thread-synchronization manually is hard and bug-prone! Use the API provided by JDK itself. – Amir Pashazadeh Jan 26 '14 at 04:54
  • lol. i found the reason. Java linkedlist is not threadsafe. I wrapped the call in synchronized(queue) and it works even without using thread.sleep. so two things that I learned: 1. java handled threading well. a tight loop in a single thread won't affect other thread. 2. linkedlist is not thread safe and race condition will cause undefined behavior (like hanging). Thanks you everyone! – Erben Mo Jan 26 '14 at 05:07

1 Answers1

4

There are multiple problems in this case.

  1. Your first implementation runs a tight loop. Depending on the platform and the threading model it may not allow any other thread to run at all. That is the reason why the producer thread is not getting a chance to put data into the queue.
  2. Your second implementation (with Thread.sleep) gives a chance for other threads to run. That is the reason why you see the consumer consumes something because the producer thread gets a chance to put data in the queue. However, even this one is open for failure because you have not synchronized the producer and consumer. See Java Tutorial - Synchronization for learning thread synchronization.
RaviH
  • 3,544
  • 2
  • 15
  • 14