sam sus
sam sus

Reputation: 21

Stop reentrant synchronization only for specified function (or code) while waiting but permit reentrant synchronizing in another function

What is the best way to achieve following goals? Let's say, given are two synchronized functions func1 and func2

public class xyz {
  private Object x1 = new Object();
  private boolean isWaiting = false;

  public void func1() {
    synchronized(this.x) {
      if(/*condition1 == expectedvalue*/1 == 1) {
        this.isWaiting = true;
        this.x1.wait();
      }
    }
  }

  public void func2() {
    synchronized(this.x1) {
      if(this.isWaiting) {
        this.x1.notifyAll();
        this.isWaiting = false;
      }
    }
  }  
}

Now my goal is that when func1 is wait()ing,that it remains NON-REENTRANT for other threads that want to enter it but at same time threads that want to execute func2 are permitted to enter it synchronized on x1 object and notify() waiting function func1 to continue its execution.

My idea was also to create nested Object synchronisation like so

public class xyz {
  private Object x1 = new Object();
  private Object x2 = new Object();
  private boolean isWaiting = false;

  public void func1() {
    synchronized(this.x2) {
      synchronized(this.x1) {
        do_here_something_synchronized();
        if(/*condition1 == expectedvalue*/1 == 1) {
          this.isWaiting = true;
          this.x1.wait();
        }
      }
    }
  }

  public void func2() {
    synchronized(this.x1) {
      do_here_something_more_synchronized();
      if(this.isWaiting) {
        this.x1.notifyAll();
        this.isWaiting = false;
      }
    }
  }  
}

Which would mean in theory that func1 would stop being REENTRANT for other threads when reaching x2 object but at same time func2 would be able to do synchronization work. Nested synchronized blocks are bad practice so far, but is there a better approach to achieve the goals?

Is there a smarter way to implement it, may be as example by java.util.concurrent.locks.* etc. without nesting it inside few synchronized blocks?

I expect some parts of code to be non-reentrant!

Upvotes: -1

Views: 77

Answers (1)

pebble unit
pebble unit

Reputation: 1224

Here's an updated attempt using only ReentrantLock and Conditions

public class Main {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition reentrantBlocker = lock.newCondition();
    private final Condition notifyContinue = lock.newCondition();

    public void waitingThread() {
        lock.lock();
        try {
            if (lock.getWaitQueueLength(notifyContinue) > 0) { //if the first time calling, should not be blocked
                System.out.println(Thread.currentThread().getName() + " wait queue for reentrant blocker: " + (lock.getWaitQueueLength(reentrantBlocker) + 1)); //wait queue including "my" wait
                System.out.println(Thread.currentThread().getName() + " awaiting on reentrant blocker");
                reentrantBlocker.await(); // Blocks reentrant by other threads and gives up lock
            }
            do_something();
            System.out.println(Thread.currentThread().getName() + " await continue");
            this.notifyContinue.await();  //wait to be notified to continue
            System.out.println(Thread.currentThread().getName() + " signal reentrant blocker");
            this.reentrantBlocker.signal(); //signals one other thread waiting for reentrant to continue
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void notifyingThread() {
        lock.lock();
        try {
            if (lock.getWaitQueueLength(notifyContinue) > 0) {
                do_something_else();
                System.out.println(Thread.currentThread().getName() + " signal continue");
                notifyContinue.signal(); // Notify waiting thread
            } else {
                System.out.println(Thread.currentThread().getName() + " do not do something else if no one is waiting for it");
            }
        } finally {
            lock.unlock();
        }
    }

    public void do_something() {
        System.out.println(Thread.currentThread().getName() + " do_something");
    }

    public void do_something_else() {
        System.out.println(Thread.currentThread().getName() + " do_something_else");
    }

    public static void main(String[] args) throws InterruptedException {
        Main m = new Main();
        var service = Executors.newFixedThreadPool(8);
        for (int i = 0; i < 4; i++) {
            Thread.sleep(2000);
            service.submit(m::notifyingThread);
        }
        for (int i = 0; i < 4; i++) {
            service.submit(m::waitingThread);
        }
        Thread.sleep(2000);
        for (int i = 0; i < 4; i++) {
            Thread.sleep(2000);
            service.submit(m::notifyingThread);
        }
        service.shutdown();
        service.awaitTermination(15, TimeUnit.SECONDS);
    }
}

This gives the output as follows:

pool-1-thread-1 do not do something else if no one is waiting for it
pool-1-thread-2 do not do something else if no one is waiting for it
pool-1-thread-3 do not do something else if no one is waiting for it
pool-1-thread-4 do not do something else if no one is waiting for it
pool-1-thread-5 do_something
pool-1-thread-5 await continue
pool-1-thread-6 wait queue for reentrant blocker: 1
pool-1-thread-6 awaiting on reentrant blocker
pool-1-thread-7 wait queue for reentrant blocker: 2
pool-1-thread-7 awaiting on reentrant blocker
pool-1-thread-8 wait queue for reentrant blocker: 3
pool-1-thread-8 awaiting on reentrant blocker
pool-1-thread-1 do_something_else
pool-1-thread-1 signal continue
pool-1-thread-5 signal reentrant blocker
pool-1-thread-6 do_something
pool-1-thread-6 await continue
pool-1-thread-2 do_something_else
pool-1-thread-2 signal continue
pool-1-thread-6 signal reentrant blocker
pool-1-thread-7 do_something
pool-1-thread-7 await continue
pool-1-thread-3 do_something_else
pool-1-thread-3 signal continue
pool-1-thread-7 signal reentrant blocker
pool-1-thread-8 do_something
pool-1-thread-8 await continue
pool-1-thread-4 do_something_else
pool-1-thread-4 signal continue
pool-1-thread-8 signal reentrant blocker

You can probably reference this as inspiration for however you want to structure your solution.

Upvotes: 0

Related Questions