Search code examples
javamultithreadinglambdadeadlock

Can a Lambda expression resolve deadlock?


I'm currently working through the Java Deadlock Tutorial and understand that the program example will always encounter a deadlock.

public class Deadlock
    {
        private static class Friend
        {
            private final String name;
            public Friend(String name) { this.name = name; }

            public String getName() {return name;}

            public synchronized void bow(Friend bower)
            {
                System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
                bower.bowBack(this);
            }

            public synchronized void bowBack(Friend bower)
            {
                System.out.format("%s: %s has bowed back to me!%n", this.name, bower.getName());
            }
        }


        public static void main(String[]args)
        {
            final Friend alphonse = new Friend("alphonse");
            final Friend gaston = new Friend("gaston");

            new Thread(new Runnable(){
                @Override
                public void run()
                {
                    alphonse.bow(gaston);
                }
            }).start();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    gaston.bow(alphonse);
                }
            }).start();

        }
    }

When I replace one of the Thread instantiations with an appropriate lambda expression: new Thread(() -> gaston.bow(alphonse)).start(); It seems to resolve the deadlocking issue. But when both Thread instantiations are replaced with lambdas, the program becomes deadlocked again.

Is there a reason behind this?


Solution

  • There's a race in the test. It will only deadlock if both threads bow at the same time.

    The time taken to output the %s: %s has bowed to me! string is enough to cause the deadlock in the first case, but not when the extra time to instantiate the lambda class is introduced.

    You can make the test deadlock when using the lambda by introducing a delay in bow:

    public synchronized void bow(Friend bower)
    {
        System.out.format("%s %s: %s has bowed to me!%n", System.currentTimeMillis(), this.name, bower.getName());
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            // ignore
        }
        bower.bowBack(this);
    }
    

    ...or by by instantiating it before beginning the test:

    public static void main(String[]args) throws InterruptedException {
        final Friend alphonse = new Friend("alphonse");
        final Friend gaston = new Friend("gaston");
    
        // instantiate the runner early
        Runnable gastonRunner = () -> gaston.bow(alphonse);
    
        new Thread(new Runnable(){
            @Override
            public void run()
            {
                alphonse.bow(gaston);
            }
        }).start();
    
    
        new Thread(gastonRunner).start();
    }