Para analisar as threads que estavam executando, lançamos mão de um Thread Dump em dois momentos, antes de tirarmos a máquina do balanceamento, e outra uma hora depois de tirá-la. No Java, para obter um Thread Dump basta executar o seguinte comando, onde o processo_id é o pid do processo Java que queremos analisar.
kill -QUIT processo_id
Com o resultado em mãos descobrimos que antes e depois de tirarmos o servidor do balanceamento havia uma thread executando (runnable) travada na linha 325 de um HashMap. Veja a descrição dessa thread abaixo:
"ExecuteThread: '94' for queue: 'default'" daemon prio=5 tid=0x00b525a0 nid=0x2c runnable [0x63bfe000..0x63bffc28]
at java.util.HashMap.get(HashMap.java:325)
(...)
Foi então que descobrimos o problemas. Procuramos no Google por esta linha e encontramos diversas pessoas com o mesmo problema e até mesmo notificações deste possível bug para Sun. Contudo, isto não é um bug. O que acontece é que a classe HashMap não é thread safe, nem mesmo para seus métodos put e get. Portanto, se diversas threads gravam e recuperam valores de um HashMap, pode ocorrer problemas como esse. De fato, se olharmos o código de HashMap, podemos perceber que o método get tem grandes possibilidades de entrar em loop infinito.
A solução recomendade pela Sun é utilizar o wrapper sincronizado para mapas Collections.synchronizedMap(new HashMap()), ou utilizar a classe ConcurrentHashMap da api de concorrência incorporada ao Java 5, a java.util.concurrent. Para quem não pode utilizar o Java 5, pode-se utilizar a versão back-port da java.util.concurrent. No nosso caso, utilizamos essa versão backport.
legal explicar que o problema em usar um mapa sincronizado como Collections.synchronizedMap(new HashMap()) é que neste caso um acesso ao mapa vai bloquear *todos* os oturos acessos. Ao utilizar as classes do Doug Lea ou do java 5 apenas o item acessado é bloqueado.
ResponderExcluir[]s