Você deve fazer este EP em Java, usando threads Java e os recursos básicos para sincronização entre threads oferecidos pela linguagem (métodos/blocos synchronized, operações wait(), notify() e notifyAll()). Se possível evite o uso de notifyAll(), que facilita os algoritmos mas tende a penalizar o desempenho. Como a idéia é trabalhar no nível das primitivas básicas para sincronização entre threads Java, não devem ser usadas as classes de mais alto nível definidas nos pacotes java.util.concurrent, java.util.concurrent.atomic e java.util.concurrent.locks.
Note que a solução vista em classe para o caso de um barbeiro só (seção
5.2.5 do livro do Andrews) usou um monitor com três variáveis de
condição. No caso mais geral de barbeiros, você certamente precisará
de mais de uma variável de condição. O desafio aqui é implementar
em Java algo equivalente a um monitor com múltiplas variáveis de
condição. Ler a seção 3.7 (e, especificamente, a subseção 3.7.3) do
livro do Doug Lea deve ajudar você nessa tarefa. Antes de fazer isso (e
antes de qualquer outra coisa), você precisa saber de uma pedra no seu
caminho: o problema do aninhamento de monitores Java (subseção 3.3.4 do
livro do Doug Lea).