javamultithreadingstarvation

Sleep a thread to avoid thread starvation


I have a thread that's running in infinite loop executing some code, which will acquire a Lock.
Meanwhile I have another thread doing scheduled task that also need to acquire the lock to do something.
To ensure that the scheduled task won't be starved in case the lock is occupied by the infinite loop for too long, I let the thread sleep for a moment after unlocking. I think that's a reasonable move and not an overkill provided that I don't mind the performance cost with the sleep?
And a follow up question too: I see someone else doing the same, but instead of sleeping for a constant time, it's sleeping for a random time ThreadUtil.sleep(RandomUtil.nextInt(5, 100)), but I couldn't fathom the benefits of doing this.

private final Lock writeLock = new ReentrantLock(true);

while (true) {
  writeLock.tryLock(100, TimeUnit.MILLISECONDS);

  try {
    doSomething();
  } finally {
    writeLock.unlock();
  }
  
  // food for everyone!
  ThreadUtil.sleep(10);
}

Some points to clarify and a follow up question:

  1. The scheduled task is acquiring the lock with a timeout (writeLock.tryLock(100, TimeUnit.MILLISECONDS)), identical to how the infinite-loop thread does it.
  2. In addtion to the scheduled task, there's actually another use case to trigger a function manually (via ws call) and acquire the lock to do something.
  3. The question: if the infinite loop thread does NOT sleep at all, I assume other threads will still eventually be executed, just that there might be a delay of uncertain amount of time?

Solution

  • At least for the scenario you described, I don't see any benefit of having random sleep time like ThreadUtil.sleep(RandomUtil.nextInt(5, 100)). Your ThreadUtil.sleep(10); will do the same job.

    Only thing is I am not sure how you scheduled task thread is acquiring the lock. I think it will also have a loop which check every x ms (let's say 10 ms) whether the lock is released or not. As long as you have only two threads (one having the lock & another trying to acquire the lock), you should be fine. But let's say we have three threads. A thread has lock & B & C threads are doing loop to acquire the lock. If B thread always comes out & try before C thread & the lock is released by A thread at that interval, C thread will always starve. The sleep & release cycle will always happen only between A & B threads as the schedule is predictable. To make it unpredictable & make sure all three threads get the chance to acquire the lock, some randomness in the sleep time is necessary.

    But I think there is a better solution than sleep & acquire. wait() & notifyAll() would be a better fit here. Thread A calls notify() on some object after specific interval & starts waiting for it. Thread B & C are already waiting on the same object. One of them will be notified & acquire the lock. Let's say C got the lock this time. If C doesn't have some scheduled task, it will again call notify() immediately. Then one of A or B would get the lock. Who acquires the lock will be decided by JVM & it will have better implementation so that one thread doesn't starve.