1

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?
waynewingorc
  • 169
  • 9
  • 1
    If you suspect that there might be more than one thread in the 'trylock...sleep' loop, a little randomness might help to get them out of lockstep. – access violation Nov 21 '22 at 02:03
  • Why not check whether the scheduled task is due to happen in the loop in your question, and dispense with the other thread and the locking altogether? – tgdavies Nov 21 '22 at 02:25
  • Have you tried calling `java.lang.Thread#yield`? – Andrey B. Panfilov Nov 21 '22 at 04:16
  • thanks @AndreyB.Panfilov. `yield` sounds like a good idea. However I'm not sure if it's somewhat nondeterministic as I saw `The thread scheduler is free to ignore yield hint`. Would that be a concern? – waynewingorc Nov 21 '22 at 05:14

1 Answers1

1

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.

aatwork
  • 2,130
  • 4
  • 17
  • thanks @aatwork I like your wait-notify solution too, especially if I can't afford sleeping 10ms in the loop as I think it will provide better throughput. I have clarified your question on `how scheduled task thread is acquiring the lock` and a follow up 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?`. Would you mind taking a look? – waynewingorc Nov 21 '22 at 03:05
  • 1
    Thanks for clarifying. And regarding your follow-up question, the answer is "unpredictable". Basically, infinite loop thread will release the lock for a nanosec or less & try to acquire the lock again. And other threads are doing tryLock(time, unit) which is essentially same infinite loop with finer controls. One of them will acquire the lock. If both acquire calls happen same time, a good JVM would allocate other thread the lock. But again, it is matter of nanosec or less. So it may or may not acquire the lock ever. I don't believe it is a good idea. – aatwork Nov 21 '22 at 04:39
  • Thanks @aatwork. I think as suggested by @AndreyB.Panfilov. under my question, `yield` might also be a good solution if the thread scheduler won't ignore it. – waynewingorc Nov 21 '22 at 05:16
  • 1
    yield() doesn't release lock. so you have to releaseLock() & yield(). Similar problem would happen again. Basically other threads should be doing tryLock() same time. if they don't, first thread will acquire the lock again. It is basically releaseLock() without sleep() that we discussed above. Your threads need same lock. So there needs to be some inter-thread communication required about when lock is acquired & released. I believe it fits better with wait() & notify(). – aatwork Nov 21 '22 at 05:37