package com.example.foo; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class SomeClass { static class Outer { Inner inner = new Inner(); public synchronized String getBoth() { return this.toString() + " - " + this.inner.toString(); } class Inner { public synchronized String getBoth() { return this.toString() + " - " + Outer.this.toString(); } @Override public synchronized String toString() { Thread.yield(); // To increase risk of deadlock return "Inner"; } } @Override public synchronized String toString() { Thread.yield(); // To increase risk of deadlock return "Outer"; } } public static void main(String[] args) throws InterruptedException { final Outer o = new Outer(); // No problem: System.out.println(o.getBoth()); // outer, inner System.out.println(o.inner.getBoth()); // inner, outer ExecutorService pool = Executors.newFixedThreadPool(4); for (int i = 0; i < 100; i++) { pool.execute(() -> { System.out.println(o.getBoth()); }); pool.execute(() -> { System.out.println(o.inner.getBoth()); }); } pool.shutdown(); if (!pool.awaitTermination(10, TimeUnit.SECONDS)) throw new RuntimeException("DEADLOCK!"); System.out.println("END"); } }