Thread Starvation in Java

Thursday, January 28th, 2010

I’ve been working with Threads in Java recently and have come across the issue of starvation. This is where a thread is unable to run because another thread is hogging the locks. To demonstrate this problem I have written a simple buffer. It has a list of objects that can be written and read. The reading thread must wait if the list is empty and the writing thread must wait if the list is full.

    1 class Buffer {

    2    volatile LinkedList data;

    3    int bufferMaxSize;

    4 

    5    public Buffer(int bufferMaxSize) {

    6       data = new LinkedList();

    7       this.bufferMaxSize = bufferMaxSize;

    8    }

    9 

   10    public synchronized T getData() throws InterruptedException {

   11       if (data.size() == 0) {

   12          try {

   13             wait();

   14          } catch (InterruptedException e) {

   15          }

   16       }

   17       notifyAll();

   18       return this.data.remove(0);

   19    }

   20 

   21    public synchronized void setData(T data)

   22       throws InterruptedException {

   23       if (this.data.size() == bufferMaxSize) {

   24          try {

   25             wait();

   26          } catch (InterruptedException e) {

   27          }

   28       }

   29       this.data.add(data);

   30       notifyAll();

   31    }

   32 }

I wrote a simple producer and consumer to test this. The problem is that until the buffer is full, the producer is starved of access to the buffer and must wait. In the example below where the max buffer size is set to 1000.

While that might be OK for a lot of situations. If you want to have fair access to the resources, then you have to manage it yourself. The notify() and notifyAll() all messages will just wake every/any waking thread. Once the lock is available then any Thread, including the one that just relinquished it, can take the lock. If that happens, then the thread must continue to wait until the lock is available.

Making your own locks rather than using the synchronized keyword will let you control which Thread is woken when a lock is relinquished but this does create an overhead that will have a performance hit. If you want alternate access to a buffer then it may be better to not have a list inside it that can buffer the data and instead just have 1 value that can’t be overwritten until it has been read.

This is only an example that shows the problem I was having but it can show itself in a number of different ways so it is something to look out for. Often using the debugger won’t show this since it is a problem of timing, putting in breakpoints slows everything down and so you can’t really see the problem.

Tags: , ,