Thread scheduling imbalance / starvation

Alan Bateman Alan.Bateman at oracle.com
Mon Apr 17 08:14:29 UTC 2023


On 16/04/2023 19:05, Martin Traverso wrote:
> :
>
> From what Ron described above, my understanding is that some threads 
> don't even get a chance to start since the first N threads keep 
> looping with no contention (acquiring and releasing the semaphore) and 
> thereby preventing the second N threads from ever being scheduled.

Yes, it's N threads spinning doing acquire/release on a semaphore with N 
permits. Another N threads (the second batch) have been started but 
haven't executed due to the greedy thread scheduling.

>
> If I add a call to Thread.yield() or Thread.sleep() for 1 ns just 
> *once* before the loop while holding the semaphore, then it works as I 
> would expect. My hypothesis is that that causes the scheduler to pick 
> one of the threads that has been waiting to start to run, which 
> subsequently makes it to the acquire() call and at some point it 
> becomes a contended acquire. The fair semaphore does its job from then on.
>
>     Thread.ofVirtual().start(() -> {
>         semaphore.acquireUninterruptibly();
>         Thread.yield();
>         semaphore.release();
>
>         while (true) {
>             semaphore.acquireUninterruptibly();
>             counter.incrementAndGet();
>             semaphore.release();
>         }
>     });
>

The effect of the Thread.yield here is that the first N threads will 
release the underlying carrier to allow the second batch of N threads to 
execute. The first attempt by threads in the second batch of N thread 
will likely block and be queued until there is an available permit. So 
you end up with 2*N threads competing to acquire permits.

-Alan


More information about the loom-dev mailing list