Third Lombok Synchronized Example

Here is another installment of my exploration into concurrency in Java. I have written about finding the Lombok library which uses annotations to reduce boilerplate in Java code.

One of the annotations provided by Lombok is @Synchronized. It is to replace the “synchronized” keyword and prevent some of the problems that can come with using the “synchronized” keyword. There are other annotations available via Lombok, but I have not looked at them.

Here is the third example of a program with a deadlock using the “synchronized” keyword that I refactored with Lombok. I got this example from the Java 2S site.

For this one I have also included the output of each of the programs.

Here is the program:

package newpackage2;

// from http://www.java2s.com/Code/Java/Threads/Threaddeadlock.htm

public class Deadlock extends Object {
    private String objID;

    public Deadlock( String id ) {
        objID = id;
    }

    public synchronized void checkOther( Deadlock other ) {
        print( "entering checkOther()" );

        try {
          Thread.sleep( 2000 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        print( "invoke 'other.action()'" );
        other.action();

        print( "leaving checkOther()" );
    }

    public synchronized void action() {
        print( "entering action()" );

        // simulate some work here
        try {
            Thread.sleep( 500 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        print( "leaving action()" );
    }

    public void print( String msg ) {
        threadPrint( "objID=" + objID + " - " + msg );
    }

    public static void threadPrint( String msg ) {
        String threadName = Thread.currentThread().getName();
        System.out.println( threadName + ": " + msg );
    }

    public static void main( String[] args ) {
        System.out.println( "Starting Deadlock" );
        final Deadlock obj1 = new Deadlock( "Thread 1" );
        final Deadlock obj2 = new Deadlock( "Thread 2" );

        Runnable runA = new Runnable() {
            public void run() {
                obj1.checkOther( obj2 );
            }
        };

        Thread thread = new Thread( runA, "A" );
        thread.start();

        try {
            Thread.sleep( 200 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        Runnable runB = new Runnable() {
            public void run() {
                obj2.checkOther( obj1 );
            }
        };

        Thread threadB = new Thread( runB, "B" );
        threadB.start();

        try {
            Thread.sleep( 5000 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        threadPrint( "finished sleeping" );

        threadPrint( "about to interrupt() threadA" );
        thread.interrupt();

        try {
            Thread.sleep( 1000 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        threadPrint( "about to interrupt() threadB" );
        threadB.interrupt();

        try {
            Thread.sleep( 1000 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        threadPrint( "did that break the deadlock?" );
        System.out.println( "Ending method main" );
    } // end method main
} // end class

Here is its output:

Starting Deadlock
A: objID=Thread 1 - entering checkOther()
B: objID=Thread 2 - entering checkOther()
A: objID=Thread 1 - invoke 'other.action()'
B: objID=Thread 2 - invoke 'other.action()'
main: finished sleeping
main: about to interrupt() threadA
main: about to interrupt() threadB
main: did that break the deadlock?
Ending method main

There is a line that says “Ending method main”, but it does not end unless you kill the program.

Here is the program refactored with Lombok:

package newpackage2;

// example from http://www.java2s.com/Code/Java/Threads/Threaddeadlock.htm
// refactored to use Lombok

import java.util.UUID;
import lombok.Synchronized;

public class DeadlockLombok  extends Object {

    private String objID;
    private final Object someObject = new Object();

    public DeadlockLombok( String id ) {
        objID = id;
    }

    // public synchronized void checkOther(DeadlockLombok other) {
    @Synchronized public void checkOther( DeadlockLombok other ) {
        print( "entering checkOther()" );

        try {
          Thread.sleep( 2000 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        print( "invoke 'other.action()'" );
        other.action();

        print( "leaving checkOther()" );
    }

    // @Synchronized public void action() { // does not work
    @Synchronized( "someObject" ) public void action() { // works
    // public synchronized void action() {
        print( "entering action()" );

        // simulate some work here
        try {
            Thread.sleep( 500 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        print( "leaving action()" );
    }

    public void print( String msg ) {
        threadPrint( "objID=" + objID + " - " + msg );
    }

    public static void threadPrint( String msg ) {
        String threadName = Thread.currentThread().getName();
        System.out.println( threadName + ": " + msg );
    }

    public static void main( String[] args ) {
        System.out.println( "Starting DeadlockLombok" );
        final DeadlockLombok obj1 = new DeadlockLombok( "Thread 1" );
        final DeadlockLombok obj2 = new DeadlockLombok( "Thread 2" );

        Runnable runA = new Runnable() {
            public void run() {
                obj1.checkOther( obj2 );
            }
        };

        Thread thread = new Thread( runA, "A" );
        thread.start();

        try {
            Thread.sleep( 200 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        Runnable runB = new Runnable() {
            public void run() {
                obj2.checkOther( obj1 );
            }
        };

        Thread threadB = new Thread( runB, "B" );
        threadB.start();

        try {
            Thread.sleep( 5000 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        threadPrint( "finished sleeping" );

        threadPrint( "about to interrupt() threadA" );
        thread.interrupt();

        try {
            Thread.sleep( 1000 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        threadPrint( "about to interrupt() threadB" );
        threadB.interrupt();

        try {
            Thread.sleep( 1000 );
        } catch ( InterruptedException x ) {
            x.printStackTrace();
        }

        threadPrint( "did that break the deadlock?" );
        System.out.println( "End method main" );
    } // end method main

} // end class

Here is the output of the refactored program:

Starting DeadlockLombok
A: objID=Thread 1 - entering checkOther()
B: objID=Thread 2 - entering checkOther()
A: objID=Thread 1 - invoke 'other.action()'
A: objID=Thread 2 - entering action()
B: objID=Thread 2 - invoke 'other.action()'
B: objID=Thread 1 - entering action()
A: objID=Thread 2 - leaving action()
A: objID=Thread 1 - leaving checkOther()
B: objID=Thread 1 - leaving action()
B: objID=Thread 2 - leaving checkOther()
main: finished sleeping
main: about to interrupt() threadA
main: about to interrupt() threadB
main: did that break the deadlock?
End method main

Image from the Wikipedia page for the Indonesian province of West Java, assumed allowed under Fair Use.