Problemas com threads

Boa tarde pessoal,

Estou tentando executar duas threads em dois objetos diferentes (não estou usando sincronização, já que não acesso uma mesma variável) para melhorar o desempenho da minha aplicação. A idéia é executar uma método simultaneamente em duas threads, mas com objetos diferentes. Entretanto, eu faço uma verificação antes do código prosseguir e essa verificação consiste em analisar se as duas threads ainda estão ativas. Segue o código base do programa:

                    	thread1 = new Thread(obj1);
                    	thread2 = new Thread(obj2);                                                            	
            			            		
                    	thread1.start();
                    	thread2.start();
                    	
                    	/*try {
    				thread1.join();
    				thread2.join();
 			} catch (InterruptedException e) { 
    				JOptionPane.showMessageDialog(null, "Ocorreu um erro com threads. Mensagem: " + e.getMessage());
    			} */                   	                    	
                    	
                    	
                    	while (thread1.isAlive() || thread2.isAlive()) {
                    		
                    	}

Percebam que as threads trabalham em dois objetos distintos, não havendo necessidade de sincronização de métodos. Entretanto, preciso que elas trabalhem simultaneamente, mas para o código prosseguir é necessário que as duas não estejam mais “vivas”. Por isso usei o while, pois o programa pára nessa linha enquanto as duas estiverem “vivas”. Mas, não está dando certo, pois a JVM lança uma exceção de ponteiro nulo. Em tese, acredito que essa é a lógica correta. Eu até tentei um join, como pode-se ver nas linhas comentadas, mas nem assim deu certo.

Gostaria de saber se existe uma maneira melhor de implementação. O que eu preciso é fazer com que o código abaixo do while seja executado depois das duas threads terem sido completadas. Mas quero manter a concorrência de execução.

Aguardo respostas

Danilo.

O certo era usar o thread1.join() e o thread2.join() mesmo.
Esse método faz a thread que o chama esperar a thread sobre o qual ele é chamado terminar a execução.

Além disso, fica difícil te ajudarmos se:

  1. Você não postar o código que vai dentro das threads (o do método run do obj1 e obj2);
  2. Você não postar a exception.

Outra coisa, Threads é um assunto para o fórum de java avançado. Vou mover o tópico mas da próxima, leia com atenção a descrição dos fóruns antes de postar.

[quote=ViniGodoy]O certo era usar o thread1.join() e o thread2.join() mesmo.
Esse método faz a thread que o chama esperar a thread sobre o qual ele é chamado terminar a execução.

Além disso, fica difícil te ajudarmos se:

  1. Você não postar o código que vai dentro das threads (o do método run do obj1 e obj2);
  2. Você não postar a exception.

Outra coisa, Threads é um assunto para o fórum de java avançado. Vou mover o tópico mas da próxima, leia com atenção a descrição dos fóruns antes de postar.[/quote]

Ok ViniGodoy, não prestei atenção nesse detalhe. rsrsrr. Mas obrigado por informar.

Quanto ao problema, é o seguinte: estou trabalhando com busca baseada em fonética. Preciso transcrever uma palavra e recuperar suas transcrições fonéticas. Dependendo da palavra, podem haver milhares de transcrições para ela. A idéia que tive foi usar duas threads para que cada uma trabalhe com uma metade da palavra. Quando as threads terminam, eu junto a lista de transcrições de cada thread, possibilitando uma melhora no desempenho. No método run() só existe uma chamada ao método de transcrição, o qual recebe a palavra a ser transcrita. Por exemplo, se o usuário digitar “Danilo”, então uma thread vai trabalhar com a metade “Dan” e a outra vai trabalhar com a metade “ilo”. Vai ser gerada uma lista para cada thread, com as respectivas transcrições, as quais serão agregadas em uma única lista. Segue o código:

Perceba que após o loop while() é feita a junção das listas retornadas pelas threads. Agora você pode perceber porque é necessário que as duas threads finalizem antes de passar pelo while().

Espero que essa explicação o ajude. Será que usando os joins resolve o problema?

Aguardo respostas.

Sim, o join() tem que funcionar.

Se você espera que as threads retornem resultado, seria melhor usar Futures e Callables:

Se você tiver várias palavras para realizar o processo, é ainda mais rápido usar também um ThreadPool (no exemplo acima, um pool é usado).

[quote=ViniGodoy]Sim, o join() tem que funcionar.

Se você espera que as threads retornem resultado, seria melhor usar Futures e Callables:

Se você tiver várias palavras para realizar o processo, é ainda mais rápido usar também um ThreadPool (no exemplo acima, um pool é usado).
[/quote]

Olá ViniGodoy,

Obrigado pelas dicas. Tentei seguir seus exemplos utilizando um ThreadPool, mas o erro persiste. Segue o código e a exceção:

                	threadPool = Executors.newCachedThreadPool();
                	Future<Set><String>> res1 = threadPool.submit(obj1);
                	Future<Set><String>> res2 = threadPool.submit(obj2);                	                               	
                	
                	Set<String> termos = new HashSet<String>();
                	
			try {						
				Set<String> termos1 = res1.get();
				Set<String> termos2 = res2.get();												
	                	
	                	for (String termo : termos1) {
	                		for (String aux: termos2) {
	                			termos.add(termo + aux);
	                		}
	                	}
                               //existem mais linhas de código, mas acho que não são necessárias, pois apenas pegam cada transcrição e faz a busca no banco de dados
                       } catch (InterruptedException e2) {						
				e2.printStackTrace();
			} catch (ExecutionException e2) {			
				e2.printStackTrace();
			}   

A exceção:

java.util.concurrent.ExecutionException: java.lang.NullPointerException
	at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
	at java.util.concurrent.FutureTask.get(FutureTask.java:83)
	at formularios.Consulta.btConsultarActionPerformed(Consulta.java:351)
	at formularios.Consulta.access$1(Consulta.java:213)
	at formularios.Consulta$2.actionPerformed(Consulta.java:181)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
	at java.awt.Component.processMouseEvent(Component.java:6263)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
	at java.awt.Component.processEvent(Component.java:6028)
	at java.awt.Container.processEvent(Container.java:2041)
	at java.awt.Component.dispatchEventImpl(Component.java:4630)
	at java.awt.Container.dispatchEventImpl(Container.java:2099)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4574)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
	at java.awt.Container.dispatchEventImpl(Container.java:2085)
	at java.awt.Window.dispatchEventImpl(Window.java:2478)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Caused by: java.lang.NullPointerException
	at soundex.SoundexFbr.fonetica(SoundexFbr.java:1065)
	at soundex.SoundexFbr.call(SoundexFbr.java:1112)
	at soundex.SoundexFbr.call(SoundexFbr.java:1)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:619)

O problema sempre está nas linhas res1.get ou res2.get. Ou seja, o método submit do ThreadPool está retornando null. O mesmo problema persiste com Threads. Não sei mais o que fazer! Não consigo identificar o problema. Se você conseguir ver alguma coisa, me avise por favor. Ficarei agradecido.

Aguardo respostas.

Danilo.

Eu investigaria essas linhas aqui:
at soundex.SoundexFbr.fonetica([color=red]SoundexFbr.java:1065[/color])
at soundex.SoundexFbr.call(SoundexFbr.java:1112)
at soundex.SoundexFbr.call(SoundexFbr.java:1)

[quote=ViniGodoy]Eu investigaria essas linhas aqui:
at soundex.SoundexFbr.fonetica([color=red]SoundexFbr.java:1065[/color])
at soundex.SoundexFbr.call(SoundexFbr.java:1112)
at soundex.SoundexFbr.call(SoundexFbr.java:1) [/quote]

Entendi. Mas esqueci de mencionar uma coisa: não é sempre que a exceção aparece. Às vezes funciona e retorna os resultados, e às vezes dá a exceção. Ou seja, será que não é problema de escalonamento de threads? Tipo, uma thread entra em execução antes da outra iniciar?

Aguardo respostas,

Danilo.

Isso pode ser, não há qualquer garantia sobre a ordem que as threads irão iniciar. Isso deveria ser um problema?

A única garantia que vc tem aí é a que o join() dá, que o trecho após o join() será executado depois da thread que levou o join acabar.

[quote=ViniGodoy]Isso pode ser, não há qualquer garantia sobre a ordem que as threads irão iniciar. Isso deveria ser um problema?

A única garantia que vc tem aí é a que o join() dá, que o trecho após o join() será executado depois da thread que levou o join acabar. [/quote]

Na verdade é um problema, pois significa que uma thread está sendo terminada antes mesmo da outra ser “startada”. Desta maneira ele passa pelo laço de repetição que evita os NullPointerException, já que uma thread já acabou a execução e outra ainda está sendo iniciada, fazendo que o isAlive das duas retorne false. Acho que é isso que está acontecendo. Me corrija se estiver errado. Mesmo com o uso de join() ainda dá esse problema.
Vou tentar corrigir isso de alguma maneira. Mas agradeço muito sua ajuda.

Até mais.

Você pode fazer:

thread1.start();
thread1.join();
thread2.start();
thread2.join();

Mas note que isso serializa as duas threads.
Nesse caso, para que ter duas threads? Se elas vão rodar uma em seguida da outra, evite o overhead de criar as threads rodando as duas tarefas numa única thread separada.

Geralmente, se a ordenação das threads importa, é porque está havendo algum problema na concepção da sua solução multi-thread. Talvez você deva inicializar algum ambiente antes de iniciar o processamento das threads, ou talvez sua solução não seja mesmo através de paralelismo.