5
votes

We use code that uses syncronized blocks around segments of code with a lot of wait and notifyAll() calls. We are trying to convert these to use Java 5 Lock.lock() and Lock.unlock() methods. How do I migrate this code to remove all the wait and notifyAll calls. I do not know the equivalent to these using the new Locking features.

Any links with examples would be appreciated.

Thanks in Advance

Eq., the following code needs to be converted to use Lock.lock() and lock.unlock The first part to remove the synchronized block is simple as I just need to call lock() method. The question is what can be done for the notifyAll() and wait methods.

     synchronized( LOCK )
            {
                while( !Thread.interrupted() )
                {
                 try
                    {

                        working = runRules();

                        if( !working )
                            LOCK.notifyAll();

                        LOCK.wait( working ? shortTimeout : longTimeout );
                    }
                    catch( final InterruptedException e )
                    {
                        Package.log.info( "Thread was interrupted.  Exiting.", e );
                        return;
                    }
                }
            }
3
If you could post a code sample, you'll get better results- there are many concurrency primitives in 1.5. Giving you advice outside of an example would be probably give you the wrong answer.Steven Fines
Thanks for your reply. I have added the code snippet you asked for.Nadeem
ReentrantLocks are more error-prone than synchronized blocks - it is easy to miss the finally block. So unless you need some specific feature - lockInterruptibly or tryLock - there is no point in converting the code. If you are determined to migrate the code to newer APIs, you should consider restructuring the code to use higher-level APIs like Semaphores, Latches or Barriers.Binil Thomas
@Binil - i agree with everything except your reference to Semaphore. i would consider a Semaphore to be the same "level" of API as Lock and wait/notify.jtahlborn

3 Answers

7
votes

Use the Conditions provided by the java.util.concurrent.locks package:

 final Object monitor = ...

 ...

 synchronized (monitor) {

     while (!condition) monitor.wait();
     ... do something ...
 }

becomes:

 final ReentrantLock lock = ...;
 final Condition cvar = lock.newCondition();

 ...

 lock.lock();

 try {

     while (!condition) cvar.await();
     ... do something ... 

 } finally {

     lock.unlock();
 }

The signalling side is pretty similar:

 synchronized (monitor) {

      ... do something ...
      monitor.notify();
 }

becomes:

 lock.lock();

 try {

     ... do something ...
     cvar.signalAll();

 } finally {

     lock.unlock();
 }
4
votes

Use Condition objects provided by the Lock.newCondition() factory method. The waiting and notification aspects of an Object's monitor were factored out into this interface.

From a migration point of view:

  • wait() -> await()
  • wait(long) -> await(long, TimeUnit.Millis) or awaitNanos(long * 10000000)
  • notify() -> signal()
  • notifyAll() -> signalAll()

However, conditions are more powerful than monitors in several ways. Firstly they are more fine grained, so you can have multiple conditions for different things. If I have a bounded blocking collection for instance, I can have a condition for full and a condition for empty, and await and notify these separately as elements are added or removed.

There are also additional await variants that allow you to await without being interrupted and await until a certain specific date (time).

The javadocs of the Condition class are very good and describe it and its use in great detail.

0
votes

Since this question is talking about notifyAll, i have tried some example of producer/consumer with phaser. I didn't used Lock, since that needs try/finally, condition object, and till unlock other thread will not work... etc...

import java.util.concurrent.Phaser;

public class ProducerConsumerExample {

    Phaser producer;
    Phaser consumers;
    volatile String array[];

    public void init() {
        producer = new Phaser(5);
        consumers = new Phaser(5);
        Consumer l1 = new Consumer("Consumer_1");
        l1.setDaemon(true);
        l1.start();
        Consumer l2 = new Consumer("Consumer_2");
        l2.setDaemon(true);
        l2.start();
        Consumer l3 = new Consumer("Consumer_3");
        l3.setDaemon(true);
        l3.start();
        Consumer l4 = new Consumer("Consumer_4");
        l4.setDaemon(true);
        l4.start();
    }

    class Consumer extends Thread {

        Consumer(String name) {
            super(name);
        }

        private void printMethod(String i) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }

        public void run() {
            while (true) {
                //make the consumers sleep till producer produces
                consumers.arriveAndAwaitAdvance();
                for (int i = 0; i < array.length; i++) {
                    printMethod(array[i]);
                }
                //alert the producer to start 
                producer.arriveAndAwaitAdvance();
                System.out.println(Thread.currentThread().getName() + " thread wakeup but will stuck with consumers.arriveAndAwaitAdvance!");

            }
        }
    }

    public void run() {
        for (int j = 0; j < 3; j++) {
            array = new String[5];
            for (int i = 0; i < array.length; i++) {
                array[i] = "Phase_" + (j + 1) + " Count_" + (i + 1);
            }
            System.out.println("Main thread pushed data.");
            //alert the consumers to start 
            consumers.arriveAndAwaitAdvance();

            //make the producer sleep till all the consumer consumes
            producer.arriveAndAwaitAdvance();   
            System.out.println("Main thread wakeup and will start pushing data...");

        }
    }

    public static void main(String[] args) {
        ProducerConsumerExample sch = new ProducerConsumerExample();
        sch.init();
        sch.run();
        System.out.println("Main thread completed, producing data.");
    }
}