Estou tendo problemas onde uma Thread está lançando IllegalMonitorStateException, mas não consigo determinar qual é a causa.
Esta é a classe:
package com.ats.ecf.cliente.service.impl;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import com.ats.ecf.cliente.service.ActiveSincronizacaoECFService;
import com.ats.ecf.cliente.service.SincronizacaoECFService;
import com.ats.framework.exception.ConfigurationException;
public class ActiveSincronizacaoECFServiceImpl implements
ActiveSincronizacaoECFService {
private static final Logger logger = Logger.getLogger(ActiveSincronizacaoECFServiceImpl.class);
private Map<String, SincronizacaoECFService> tarefas;
private final Set<String> agendados = new HashSet<String>();
private final Thread thread = new Thread("Sincronização de dados de ECF") {
@Override
public void run() {
while (this.isAlive()) {
executaSincronizacao();
}
}
};
public void inicializa() {
thread.start();
}
@Override
public void agenda(String nomeTarefa) {
synchronized (this.agendados) {
this.agendados.add(nomeTarefa);
this.notify(); // Avisa que houve um agendamento.
}
}
@Override
public void sincroniza() {
for (String nomeTarefa : this.getTarefas().keySet()) {
this.agenda(nomeTarefa);
}
}
private boolean checkAgenda(String nomeTarefa) {
synchronized (this.agendados) {
boolean marcado = this.agendados.contains(nomeTarefa);
this.agendados.remove(nomeTarefa);
return marcado;
}
}
private boolean existeAgendamento() {
synchronized (this.agendados) {
return !this.agendados.isEmpty();
}
}
private void aguardaAgendamento() {
logger.debug("Aguardando agendamento.");
while (!this.existeAgendamento()) {
try {
this.wait(); // Aguarda até que algum agendamento ocorra.
} catch (InterruptedException e) {
logger.debug("Pausa para agendamento interrompida.", e);
}
}
}
private void executaSincronizacao() {
this.aguardaAgendamento();
logger.debug("Executando tarefas de sincronização agendadas.");
for (Map.Entry<String, SincronizacaoECFService> entry : this.getTarefas().entrySet()) {
if (this.checkAgenda(entry.getKey())) {
if (logger.isDebugEnabled()) {
logger.debug("Executando " + entry.getKey());
}
entry.getValue().sincroniza();
}
}
}
public Map<String, SincronizacaoECFService> getTarefas() {
if (this.tarefas == null) {
throw new ConfigurationException("Tarefas não informadas.");
}
return this.tarefas;
}
public void setTarefas(Map<String, SincronizacaoECFService> tarefas) {
this.tarefas = tarefas;
}
}
Este é o teste unitário:
package com.ats.ecf.cliente.service.test;
import com.ats.ecf.cliente.service.SincronizacaoECFService;
import com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl;
import com.ats.framework.util.map.MapBuilder;
import junit.framework.TestCase;
public class ActiveSincronizacaoECFServiceTest extends TestCase {
public void testSincronizacao() throws InterruptedException {
ActiveSincronizacaoECFServiceImpl service = new ActiveSincronizacaoECFServiceImpl();
SincronizacaoTestClass sinc1 = new SincronizacaoTestClass();
SincronizacaoTestClass sinc2 = new SincronizacaoTestClass();
service.setTarefas(new MapBuilder<String, SincronizacaoECFService>()
.put("a", sinc1).put("b", sinc2).getMap());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.inicializa();
Thread.sleep(500);
assertFalse(sinc1.isInvocado());
assertFalse(sinc2.isInvocado());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.agenda("a");
Thread.sleep(500);
assertTrue(sinc1.isInvocado());
assertFalse(sinc2.isInvocado());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.agenda("b");
Thread.sleep(500);
assertFalse(sinc1.isInvocado());
assertTrue(sinc2.isInvocado());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.agenda("a");
service.agenda("b");
Thread.sleep(500);
assertTrue(sinc1.isInvocado());
assertTrue(sinc2.isInvocado());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.sincroniza();
Thread.sleep(500);
assertTrue(sinc1.isInvocado());
assertTrue(sinc2.isInvocado());
}
private static class SincronizacaoTestClass implements SincronizacaoECFService {
private boolean invocado;
@Override
public void sincroniza() {
this.setInvocado(true);
}
public boolean isInvocado() {
return invocado;
}
public void setInvocado(boolean invocado) {
this.invocado = invocado;
}
}
}
Esta é a exceção:
Exception in thread "Sincronização de dados de ECF" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl.aguardaAgendamento(ActiveSincronizacaoECFServiceImpl.java:66)
at com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl.executaSincronizacao(ActiveSincronizacaoECFServiceImpl.java:74)
at com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl.access$0(ActiveSincronizacaoECFServiceImpl.java:73)
at com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl$1.run(ActiveSincronizacaoECFServiceImpl.java:24)
[quote=haroldo-ok-ats]Estou tendo problemas onde uma Thread está lançando IllegalMonitorStateException, mas não consigo determinar qual é a causa.
Esta é a classe:
package com.ats.ecf.cliente.service.impl;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import com.ats.ecf.cliente.service.ActiveSincronizacaoECFService;
import com.ats.ecf.cliente.service.SincronizacaoECFService;
import com.ats.framework.exception.ConfigurationException;
public class ActiveSincronizacaoECFServiceImpl implements
ActiveSincronizacaoECFService {
private static final Logger logger = Logger.getLogger(ActiveSincronizacaoECFServiceImpl.class);
private Map<String, SincronizacaoECFService> tarefas;
private final Set<String> agendados = new HashSet<String>();
private final Thread thread = new Thread("Sincronização de dados de ECF") {
@Override
public void run() {
while (this.isAlive()) {
executaSincronizacao();
}
}
};
public void inicializa() {
thread.start();
}
@Override
public void agenda(String nomeTarefa) {
synchronized (this.agendados) {
this.agendados.add(nomeTarefa);
this.notify(); // Avisa que houve um agendamento.
}
}
@Override
public void sincroniza() {
for (String nomeTarefa : this.getTarefas().keySet()) {
this.agenda(nomeTarefa);
}
}
private boolean checkAgenda(String nomeTarefa) {
synchronized (this.agendados) {
boolean marcado = this.agendados.contains(nomeTarefa);
this.agendados.remove(nomeTarefa);
return marcado;
}
}
private boolean existeAgendamento() {
synchronized (this.agendados) {
return !this.agendados.isEmpty();
}
}
private void aguardaAgendamento() {
logger.debug("Aguardando agendamento.");
while (!this.existeAgendamento()) {
try {
this.wait(); // Aguarda até que algum agendamento ocorra.
} catch (InterruptedException e) {
logger.debug("Pausa para agendamento interrompida.", e);
}
}
}
private void executaSincronizacao() {
this.aguardaAgendamento();
logger.debug("Executando tarefas de sincronização agendadas.");
for (Map.Entry<String, SincronizacaoECFService> entry : this.getTarefas().entrySet()) {
if (this.checkAgenda(entry.getKey())) {
if (logger.isDebugEnabled()) {
logger.debug("Executando " + entry.getKey());
}
entry.getValue().sincroniza();
}
}
}
public Map<String, SincronizacaoECFService> getTarefas() {
if (this.tarefas == null) {
throw new ConfigurationException("Tarefas não informadas.");
}
return this.tarefas;
}
public void setTarefas(Map<String, SincronizacaoECFService> tarefas) {
this.tarefas = tarefas;
}
}
Este é o teste unitário:
package com.ats.ecf.cliente.service.test;
import com.ats.ecf.cliente.service.SincronizacaoECFService;
import com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl;
import com.ats.framework.util.map.MapBuilder;
import junit.framework.TestCase;
public class ActiveSincronizacaoECFServiceTest extends TestCase {
public void testSincronizacao() throws InterruptedException {
ActiveSincronizacaoECFServiceImpl service = new ActiveSincronizacaoECFServiceImpl();
SincronizacaoTestClass sinc1 = new SincronizacaoTestClass();
SincronizacaoTestClass sinc2 = new SincronizacaoTestClass();
service.setTarefas(new MapBuilder<String, SincronizacaoECFService>()
.put("a", sinc1).put("b", sinc2).getMap());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.inicializa();
Thread.sleep(500);
assertFalse(sinc1.isInvocado());
assertFalse(sinc2.isInvocado());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.agenda("a");
Thread.sleep(500);
assertTrue(sinc1.isInvocado());
assertFalse(sinc2.isInvocado());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.agenda("b");
Thread.sleep(500);
assertFalse(sinc1.isInvocado());
assertTrue(sinc2.isInvocado());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.agenda("a");
service.agenda("b");
Thread.sleep(500);
assertTrue(sinc1.isInvocado());
assertTrue(sinc2.isInvocado());
sinc1.setInvocado(false);
sinc2.setInvocado(false);
service.sincroniza();
Thread.sleep(500);
assertTrue(sinc1.isInvocado());
assertTrue(sinc2.isInvocado());
}
private static class SincronizacaoTestClass implements SincronizacaoECFService {
private boolean invocado;
@Override
public void sincroniza() {
this.setInvocado(true);
}
public boolean isInvocado() {
return invocado;
}
public void setInvocado(boolean invocado) {
this.invocado = invocado;
}
}
}
Esta é a exceção:
Exception in thread "Sincronização de dados de ECF" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl.aguardaAgendamento(ActiveSincronizacaoECFServiceImpl.java:66)
at com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl.executaSincronizacao(ActiveSincronizacaoECFServiceImpl.java:74)
at com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl.access$0(ActiveSincronizacaoECFServiceImpl.java:73)
at com.ats.ecf.cliente.service.impl.ActiveSincronizacaoECFServiceImpl$1.run(ActiveSincronizacaoECFServiceImpl.java:24)
[/quote]
java.lang.IllegalMonitorStateException
Essa exception é pq em algum lugar do seu codigo vc esta usando “wait ou notfy” de algum objeto não sincronizado. Voce so pode usar esses métodos em um contexto syncrhonized.
E por um acaso é aqui que esta gerando á exceção:
private void aguardaAgendamento() {
logger.debug("Aguardando agendamento.");
while (!this.existeAgendamento()) {
try {
this.wait(); // ==> AQUI <==
} catch (InterruptedException e) {
logger.debug("Pausa para agendamento interrompida.", e);
}
}
}
[quote=evertonsilvagomesjava]E por um acaso é aqui que esta gerando á exceção:
private void aguardaAgendamento() {
logger.debug("Aguardando agendamento.");
while (!this.existeAgendamento()) {
try {
this.wait(); // ==> AQUI <==
} catch (InterruptedException e) {
logger.debug("Pausa para agendamento interrompida.", e);
}
}
}
[/quote]
Hã… sim, eu notei isso. 
O wait() só é chamado em um único lugar, e apenas por uma única Thread, o notify() só é chamado em um único lugar.
A questão é: porquê a exceção está sendo gerada, e como corrigí-la.
[quote=haroldo-ok-ats][quote=evertonsilvagomesjava]E por um acaso é aqui que esta gerando á exceção:
private void aguardaAgendamento() {
logger.debug("Aguardando agendamento.");
while (!this.existeAgendamento()) {
try {
this.wait(); // ==> AQUI <==
} catch (InterruptedException e) {
logger.debug("Pausa para agendamento interrompida.", e);
}
}
}
[/quote]
Hã… sim, eu notei isso. 
O wait() só é chamado em um único lugar, e apenas por uma única Thread, o notify() só é chamado em um único lugar.
A questão é: porquê a exceção está sendo gerada, e como corrigí-la.[/quote]
Pra corrigir vc tem que sincronizar “this”.
Ah, OK, fazendo as alterações, abaixo, a classe executou exatamente da forma esperada:
@Override
public void agenda(String nomeTarefa) {
synchronized (this.agendados) {
this.agendados.add(nomeTarefa);
synchronized (this) {
this.notify(); // Avisa que houve um agendamento.
}
}
}
private void aguardaAgendamento() {
logger.debug("Aguardando agendamento.");
while (!this.existeAgendamento()) {
try {
synchronized (this) {
this.wait(); // Aguarda até que algum agendamento ocorra.
}
} catch (InterruptedException e) {
logger.debug("Pausa para agendamento interrompida.", e);
}
}
}
Valeu pela ajuda! 
[quote=haroldo-ok-ats]Ah, OK, fazendo as alterações, abaixo, a classe executou exatamente da forma esperada:
@Override
public void agenda(String nomeTarefa) {
synchronized (this.agendados) {
this.agendados.add(nomeTarefa);
synchronized (this) {
this.notify(); // Avisa que houve um agendamento.
}
}
}
private void aguardaAgendamento() {
logger.debug("Aguardando agendamento.");
while (!this.existeAgendamento()) {
try {
synchronized (this) {
this.wait(); // Aguarda até que algum agendamento ocorra.
}
} catch (InterruptedException e) {
logger.debug("Pausa para agendamento interrompida.", e);
}
}
}
Valeu pela ajuda!
[/quote]
Isso ai, tinha colocado a resposta dentro do “quote” kk.