Самый известный подход, который обеспечивает защиту от дедлока – захват и освобождение мониторов в строго определённом порядке.
Например имеются два монитора A и B захват которых осуществляется в порядке AB, соответсвенно, чтобы избежать дедлока, необходимо производить освобождение мониторов в обратном порядке - BA.
В таком случае ни один поток не сможет попасть на ожидание монитора B, успешно захватив при этом A.
То есть самый простой способ получить deadlock - нарушить данное правило. Но при этом всё равно нет гарантии что deadlock будет получен сразу же, для этого может потребоваться довольно длительное время.
Чтобы точно с первого раза выстрелить себе в ногу, нужно обеспечить ситуацию, когда оба потока окажутся на этапе между захватами одного и другого ресурса в одно время.
Для этого можно использовать CyclicBarrier, пример кода приведён ниже.
Теперь вы знаете как делать не надо! Многопоточность довольно важная тема, которую любят спрашивать на собеседованиях, хотя на практике в некоторых проектах напрямую с многопоточностью работать не приходится (зависит от специфики).
Из своего опыта проведения собеседований могу уверенно сказать что плавают в этой теме большинство кандидатов :)