Dúvida com Threads

Bom dia pessoal,

Minha dúvida é a seguinte:

Se eu criar uma thread para por exemplo escrever algo na porta serial é certo por exemplo eu colocar um while(true) dentro do método run() da thread e criar um método extra do tipo enviarMensagem(String mensagem), exemplo:

class minhaThread() extends Thread{


 run(){
     while(true);
 }

 public void enviarMensagem(String mensagem){
           /* método de outra classe especialista em se comunicar com a porta serial */
           portaSerial.saidaSerial(mensagem);
 }

}

isso é válido? é o único jeito? tem outro jeito? qual???

Eu quero que ela seja tipo um “Serviço” para que quando eu queria de qualquer ponto da minha aplicação enviar uma mensagem eu chamar o método enviarMensagem(“Hello world”).

Para ser um serviço teria que setar como daemon, é isso?

Se eu crio isso como serviço e quando eu for precisar enviar uma mensagem e se essa thread está em estado dormindo, o que ocorre com minha mensagem? Não será enviada? Quem acorda ela para enviar a mensagem?

Essa é minha dúvida!

obrigado!

}

Não precisa setar pra daemon, o que você precisa é usar um objeto que vai monitorar quando há mensagens novas. Dentro do run() você vai precisar utilizar o

syncronized(monitor){
   monitor.wait();
}

assim a thread vai ficar naquele wait, esperando por um sinal, quando você tiver uma mensagem pra enviar, é só você fazer o seguinte código:

synchronized(monitor){
   monitor.notify();
}

esse código vai avisar à thread que está com o wait, para continuar executando.

É perfeitamente válido.

Thread é thread.
Objeto é objeto, não confunda as duas coisas.

Uma thread pode percorrer vários objetos, inclusive objetos que outras thread estão percorrendo. Aí que entra a sincronização.

[quote=ViniGodoy]É perfeitamente válido.

Thread é thread.
Objeto é objeto, não confunda as duas coisas.

Uma thread pode percorrer vários objetos, inclusive objetos que outras thread estão percorrendo. Aí que entra a sincronização.[/quote]

Boa tarde, soaresinfo e ViniGodoy

Vocês poderiam se possível for me exemplificar isso melhor, no caso sobre wait, por exemplo, eu tenho um objeto que tem um método que vai chamar minha thread:

class meuObjeto(){

   Thread t = new Thread();
   ...
   t.start();

   ...

   t.enviarMensagem("Hello world");

}

como faço com esse wait e notify?

Desde já agradeço a ajuda de vocês!!!

Muito Obrigado mesmo…

Olá ViniGodoy…

Deu uma olhada no código e mesmo eu não tendo um conhecimento tão bom assim em java posso dizer ( acredito ) que é um código muito prático e excelente.

Porém, o que estou fazendo é um pouco diferente, na verdade é algo assim:

Em ambiente linux :

Monitoro a serial na ttyS0 -> faço o processamento do caracter que chegar -> redireciono para ttyS1

Para vc entender melhor é um equipamento MINI PC que vai rodar linux ubuntu usando java, esse equipamento vai ter um display LCD ( Cristal Liquido ) e um teclado de membranas na parte frontal…

Eu vou capturar o que for digitado nesse teclado de membrana e atraves de um microcontrolador, no caso um PIC, e vou mandar via serial para o PC, caracter a caracter…

Vou ter uma thread ouvindo a porta serial no ttyS0 usando a API RXTX ( http://rxtx.qbang.org/wiki/index.php/Event_based_two_way_Communication)

Atraves do padrão Observer vou ter uma classe que vai ser avisada quando chegar um desses caracteres:


         while(true){
                  if(resposta!=0x00){
                         /*Preciso mandar para uma outra thread ( p/ ttyS1 ) a mensagem*/
                         minhaThread.enviarMensagem(resposta);
                  }

         }



	@Override
	public void update(Observable arg0, Object arg1) {
                /*Chegou caracter da serial ttyS0 */
		resposta=(Character) arg1;


		
	} 

A pergunta é:

Posso colocar um notify nesse trecho para acordar a thread e no while chamar o método da thread? Ou estou usando o conceito de thread não está muito bem colocado do jeito que eu fiz?

Obrigado vini, mesmo se não me der resposta, já me ajudou bastante, valeu mesmo!!!

Sim. A classe que implementa o observer e espera o caracter dá o wait().
E no método onde a mensagem do observer vem ela dá o notifyAll().

Você fará isso na mesma classe, mas serão 2 threads que operarão sobre ela:

  1. Quando vc chamar o método para esperar o caracter, a thread que chama o método irá invocar o wait();
  2. Quando o observer for disparado, é a thread que lê a conexão que irá chamar o método do listener da classe observer.

[quote=ViniGodoy]Sim. A classe que implementa o observer e espera o caracter dá o wait().
E no método onde a mensagem do observer vem ela dá o notifyAll().

Você fará isso na mesma classe, mas serão 2 threads que operarão sobre ela:

  1. Quando vc chamar o método para esperar o caracter, a thread que chama o método irá invocar o wait();
  2. Quando o observer for disparado, é a thread que lê a conexão que irá chamar o método do listener da classe observer. [/quote]

Boa tarde vini,

Tentei fazer o que disse, olhe estou dizendo tentei viu, pois acho que não é o que me disse, pois não funcionou não.

O problema é que quando chega um caracter pela serial o médodo da minha classe observer é disparado a Thread ClienteSerial percebe isso dentro do seu método run que está com while(true), chama o notifyAll(), porém, a thread que fio dormir ao chamar wait(), não volta a executar não…

o que poderá ser?


public class InicialDisplayBO {
	
	private Controller controller;
	@SuppressWarnings("unused")
	private InicialDisplay inicialDisplay;
	private char resposta = NUL;
	private boolean naoProcessou = true;
	private boolean naoTerminou = true;
	private Thread t1;
	private Thread t2;

	
	

	public InicialDisplayBO(InicialDisplay inicialDisplay, Controller controller) {
		super();
		this.controller = controller;
		this.inicialDisplay = inicialDisplay;
		t1 = new Thread(new ClienteSerial(),"Cliente Serial");
		t2 = new Thread(new ProcessaSerial(),"Processa Serial");
		t2.start();
		t1.start();
		
		System.out.println("Inicializando leitura da tecla FUNCTION");
		
		while(naoTerminou);
		
		System.out.println("Finalizando leitura da tecla FUNCTION");

	}

	
	
	
	private synchronized void processaTecla() throws InterruptedException{
		System.out.println("Processa Tecla: " + resposta);
		switch(resposta){
		
			case FUNCTION:{
				System.out.println("FUNCTION pressionado");
				resposta=NUL;
				this.controller.setDisplay(Display.SAIR);
				naoTerminou = false;

			}break;
	
		}
		
	}
	
	
	private  void verificaSerial() throws InterruptedException{
		
		while(resposta==NUL){

			synchronized (this){
				wait();
			}

		}

		
	}
	
	
	private class ProcessaSerial implements Runnable{

		@Override
		public void run() {

			while(true){

				try {
					verificaSerial();
					processaTecla();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}
		
	}

	private class ClienteSerial implements Runnable,Observer{

		PortaSerial portaSerial = new PortaSerial();
		PortaSerialCOM portaSerialCOM = new PortaSerialCOM();
		
		@Override
		public void run() {
			
			portaSerial.setPorta("/dev/ttyS0");
			portaSerialCOM.addObserver(this);
			try {
				portaSerialCOM.abrirPorta(portaSerial);
				portaSerialCOM.lerMensagem();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
	
			while(true){
				if(resposta!=NUL && naoProcessou){
					
					synchronized(this){
						notifyAll();
					}
					naoProcessou=false;
				
					
				}
				
				
			}
		}

		@Override
		public void update(Observable arg0, Object arg1) {

			/**
			 * Recebe caracter que vem pela serial
			 */
			
			resposta = (Character) arg1;
		}
		
	}

}


Obrigado!!!

Alguns comentários:

  1. Aquele while (naoterminou) deve jogar seu processador a 100%. No lugar disso, se quiser esperar as threads, use o método join().
  2. Você não pode compartilhar variáveis entre threads sem usar sincronização, ou sem que elas sejam volatile;
  3. É o método update que deveria chamar notifyAll().
  4. Seria bom criar uma fila e implementar o produtor/consumidor. A thread que monitora a serial é a produta. Ela deve simplesmente ler, capturar o caratere e dispara-lo aos seus listeners. A classe que processa é a consumidora. Ela aguarda uma tecla chegar na fila e a processa. Apenas a fila terá que ser sincronizada, para isso, use uma BlockingQueue. A vantagem é que a própria queue já controla para você os wait, cabendo apenas a você chamar o notifyAll().

Resumindo:
classe ClienteSerial

  • Lê uma tecla do cliente;
  • Gera um evento de tecla e põe na fila;
  • Dá o notifyAll avisando que um novo evento foi colocado

Classe processa serial

  • Aguarda um evento entrar na fila (a própria blockingQueue faz o wait);
  • Processa esse evento.
  • Encaminha o resultado para outra classe (pode ser a iniciaDisplayBD), na forma de mensagens (algo como DisplayArrived, já com o display completamente montado).

Esse seu software é extremamente similar com os que eu trabalhava na época da Siemens. Bateu até saudades.

[quote=ViniGodoy]Alguns comentários:

  1. Aquele while (naoterminou) deve jogar seu processador a 100%. No lugar disso, se quiser esperar as threads, use o método join().
  2. Você não pode compartilhar variáveis entre threads sem usar sincronização, ou sem que elas sejam volatile;
  3. É o método update que deveria chamar notifyAll().
  4. Seria bom criar uma fila e implementar o produtor/consumidor. A thread que monitora a serial é a produta. Ela deve simplesmente ler, capturar o caratere e dispara-lo aos seus listeners. A classe que processa é a consumidora. Ela aguarda uma tecla chegar na fila e a processa. Apenas a fila terá que ser sincronizada, para isso, use uma BlockingQueue. A vantagem é que a própria queue já controla para você os wait, cabendo apenas a você chamar o notifyAll().

Resumindo:
classe ClienteSerial

  • Lê uma tecla do cliente;
  • Gera um evento de tecla e põe na fila;
  • Dá o notifyAll avisando que um novo evento foi colocado

Classe processa serial

  • Aguarda um evento entrar na fila (a própria blockingQueue faz o wait);
  • Processa esse evento.
  • Encaminha o resultado para outra classe (pode ser a iniciaDisplayBD), na forma de mensagens (algo como DisplayArrived, já com o display completamente montado).

Esse seu software é extremamente similar com os que eu trabalhava na época da Siemens. Bateu até saudades.[/quote]

Vini,

Muito obrigado, vou me esforçar para tentar compreender o que vc está me passando. Eu achei que era simples, mas preciso ser sincero, sou leigo com threads e com java tb sou novato. Eu estudo por conta e tento sempre seguir boas práticas de programação. Apesar de parecer simples pra vc tudo isso que vc disse, pra mim é um monte de incógnitas. Até sei mais ou menos o que seja o produtor/consumidor, volatile( bem pouco ), agora blockingQueue já não faço idéia.

No entanto fico feliz de saber que pelo menos algo que eu tentei fazer somente com suas explicações te lembrou de seu antigo trabalho na Siemens, acho que estou no caminho certo.

Só mais uma coisa, o fato da thread não acordar tem a ver com tudo isso que vc disse né?
E por um acaso vc não teria nenhum artigo ou trechos em artigos que possam me ajudar né?

Obrigado!!!
Muito obrigado mesmo!!!

[quote=ViniGodoy]Alguns comentários:

  1. Aquele while (naoterminou) deve jogar seu processador a 100%. No lugar disso, se quiser esperar as threads, use o método join().
  2. Você não pode compartilhar variáveis entre threads sem usar sincronização, ou sem que elas sejam volatile;
  3. É o método update que deveria chamar notifyAll().
  4. Seria bom criar uma fila e implementar o produtor/consumidor. A thread que monitora a serial é a produta. Ela deve simplesmente ler, capturar o caratere e dispara-lo aos seus listeners. A classe que processa é a consumidora. Ela aguarda uma tecla chegar na fila e a processa. Apenas a fila terá que ser sincronizada, para isso, use uma BlockingQueue. A vantagem é que a própria queue já controla para você os wait, cabendo apenas a você chamar o notifyAll().

Resumindo:
classe ClienteSerial

  • Lê uma tecla do cliente;
  • Gera um evento de tecla e põe na fila;
  • Dá o notifyAll avisando que um novo evento foi colocado

Classe processa serial

  • Aguarda um evento entrar na fila (a própria blockingQueue faz o wait);
  • Processa esse evento.
  • Encaminha o resultado para outra classe (pode ser a iniciaDisplayBD), na forma de mensagens (algo como DisplayArrived, já com o display completamente montado).

Esse seu software é extremamente similar com os que eu trabalhava na época da Siemens. Bateu até saudades.[/quote]

Boa tarde vini,

Dei uma olhada na net, estudei um código que achei e implementei dessa forma:


public class InicialDisplayBO {
	
	private Controller controller;
	@SuppressWarnings("unused")
	private InicialDisplay inicialDisplay;
	public volatile boolean rodando = true;
	private BlockingQueue<Integer> fila = new LinkedBlockingDeque<Integer>(10);
	private Thread t1;
	private Thread t2;
	
	

	public InicialDisplayBO(InicialDisplay inicialDisplay, Controller controller) {
		super();
		this.controller = controller;
		this.inicialDisplay = inicialDisplay;
		t1 = new Thread(new ClienteSerial(fila),"Cliente Serial");
		t2 = new Thread(new ProcessaSerial(fila),"Processa Serial");
		t2.start();
		t1.start();

		try {
			t2.join();
			t1.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}


	}

	
	
	
	private void processaTecla(Integer resposta) throws InterruptedException{

		char[] caracteres = Character.toChars(resposta.intValue());

		System.out.println("Processa Tecla: " + caracteres[0]);
		
		switch(resposta){
			case FUNCTION:{
				resposta=NUL;
				this.controller.setDisplay(Display.SAIR);
				this.rodando = false;
			}break;
	
		}
		
	}

	
	private class ProcessaSerial implements Runnable{
		
		private Integer resposta;
		private BlockingQueue<Integer> fila;

		public ProcessaSerial(BlockingQueue<Integer> fila){
			this.fila = fila;
		}

		@Override
		public void run() {
			while(rodando){
				try {
					resposta = fila.take();
					processaTecla(resposta);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		

		
	}

	private class ClienteSerial implements Runnable,Observer{

		private char resposta=0;
		private BlockingQueue<Integer> fila;
		
		private PortaSerial portaSerial = new PortaSerial();
		private PortaSerialCOM portaSerialCOM = new PortaSerialCOM();
		
		public ClienteSerial(BlockingQueue<Integer> fila){
			this.fila = fila;
			
			portaSerial.setPorta("/dev/ttyS0");
			portaSerialCOM.addObserver(this);
			try {
				portaSerialCOM.abrirPorta(portaSerial);
				portaSerialCOM.lerMensagem();
			} catch (Exception e) {
			
				e.printStackTrace();
			}
		}

		
		private void finalizar(){
			try {
				portaSerialCOM.fecharPorta();
			} catch (Exception e) {
			
				e.printStackTrace();
			}
		}
		
		@Override
		public void run() {
			while(rodando){
				synchronized (this) {
					if(resposta==0){
						try {
							wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}	
			}
			finalizar();
		}

		@Override
		public void update(Observable arg0, Object arg1) {
			

			resposta = (Character) arg1;
			
			try {
				char tmp = resposta;
				fila.put((int) resposta);
				resposta=0;
				try {
					portaSerialCOM.enviarMensagem(tmp);
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			
			synchronized (this) {
				notifyAll();
			}
			


		}
		
	}

}


Funcionou!!!

Gostaria de apenas tirar uma dúvida, na thread criada para ouvir a porta serial ( a qual não está nesse meu código ) e que inclusive é uma thread baseada em um artigo aqui do guj
http://javafree.uol.com.br/topic-858960-Protocolo-de-comunicacao-com-API-Javacomm.html ocorre a seguinte situação:



/método RUN da thread de leitura
public void run(){
  try {
    Thread.sleep(5000);
  } catch (Exception e) {
     System.out.println("Erro. Status = " + e ); 
 }
}

Ou seja, a thread morre após 5 segundos, não é isso? Mas mesmo assim, ela continua ouvindo a serial por estar na memória, é isso, não é?
A pergunta é: isso é está certo? É assim mesmo, ou foi apenas para fins didáticos?

A outra pergunta é:

No código que postei acima, utilizei as Threads:


		t1 = new Thread(new ClienteSerial(fila),"Cliente Serial");
		t2 = new Thread(new ProcessaSerial(fila),"Processa Serial");

Posso separá-las dessa classe?


public class InicialDisplayBO {

...

}

A intenção é que outras classes utiliem a leitura e o retorno de caracteres seriais ( ecoem de volta ao equipamento).

Se puder me ajudar com isso tb, mas desde já agradeço imensamente sua ajuda e de todos que de uma forma ou de outra me ajudaram.

Obrigado