javaconcurrencylocks

Same Thread Relocking Reentrant lock


I wrote a piece of code that is supposed to print 'ping pong' using two different methods running on different threads.

Sharing the working code:

package threading;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PingPong {

  private static volatile int times;
  private static volatile boolean ping;
  private static final Lock lock = new ReentrantLock();

  private static void ping() {
    int i = 1;
    while (i < times) {
        lock.lock();

        if (ping) {
          System.out.print("ping ");
          ping = !ping;
          i += 1;
        }

        lock.unlock();
    }
  }

  private static void pong() {
    int i = 1;
    while (i < times) {
        lock.lock();

        if (!ping) {
          System.out.println("pong");
          ping = !ping;
          i += 1;
        }

        lock.unlock();
    }
  }

  public static void main(String[] args) {
    times = 10;
    ping =true;

    Thread pingThread = new Thread(PingPong::ping);
    Thread pongThread = new Thread(PingPong::pong);

    pingThread.start();
    pongThread.start();
  }
}

The output in this case:

ping pong
ping pong
ping pong
ping pong
ping pong
ping pong
ping pong
ping pong
ping pong

If I replace the while loops in the ping and pong methods with for loops, the same thread seems to be locking again and again not giving the second one a fair chance to enter. The output in that case is inconsistent and comes up different everytime. Does while loop execute things differently that gives the second thread a chance to acquire the lock ?


Solution

  • I can't see there being any particular difference between 'for' and 'while'. But your code as written, even with 'while', does not guarantee the behaviour you want.

    From the ReentrantLock documentation:

    The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order.

    You should use a 'fair' lock to avoid starving one thread. Even so, there's no real guarantee that while one thread owns the lock, the other one will have got to its own lock() call so as to ensure it is next in line as soon as unlock() is executed.