Pesquisar este blog

quinta-feira, 25 de abril de 2013

Programação concorrente em Java (Threads)

Programacao concorrente:

Quando estamos usando o computador, muitas vezes estamos fazendo mais de uma coisa ao mesmo tempo, mas nem percebemos. Ouvimos musica, navegamos na internet e muitas vezes editamos documentos, tudo ao mesmo tempo.
Isso ja era possivel, mesmo na epoca dos processadores de um nucleo, coisa que so era possível devido as threads, ou programação concorrente.
Vamos comecar pelo o que e programação concorrente? 
Dizemos que um programa concorrente, e aquele que possui duas, ou mais partes do codigo, que podem ser executadas paralelamente.
Imagine que você esteja fazendo um programa e neste programa, você vai abrir um arquivo muito pesado. Leitura de arquivo e uma coisa bem lenta, e como você sabe que esta operação sera demorada, você quer mostrar uma barra de progresso, para o seu usuário.
Isto so e possivel devido a programação concorrente, que e realizada pelas threads. Cada thread é uma parte do programa que esta rodando, em java a maquina virtual é quem faz uma interface com o sistema operacional, que é quem realmente cuida de como e quando uma thread será executada.
Em java existem duas formas de conseguir rodar um código em paralelo:
  • Implementando a interface java.lang.Runnable
  • Extendendo a classe java.lang.Thread
A intereface Runnable define um método run, que e onde deveremos colocar o código que sera executado pela thread. A classe Thread implementa a interface Runnable.
Uma thread em java pode estar em um dos status mostrados a seguir:
  • NEW: thread criada mas ainda nao iniciada
  • RUNNABLE: quando a thread foi iniciada e pode ser executada pela maquina virtual
  • BLOCKED: quando a thread esta bloqueada esperando pela liberação de um lock para continuar a sua execução.
  • WAITING: Uma thread que esta esperando a execução de uma outra thread terminar para continuar a sua execução.
  • TIMED_WAITING: o mesmo que waiting, porem com um determinado tempo maximo de espera.
  • TERMINATED: uma thread que ja completou a sua execução.
Quando criamos uma thread ela esta no status new, para iniciarmos uma thread executamos o método start, com isso ela passa para o status de runnable.
Abaixo temos um exemplo de como criar uma thread:
public class ExemploConcorrencia implements Runnable{
     public void run{
            // Codigo a ser executado
    }
 Para executar o codigo acima faremos:
new ExemploConcorrencia().start();
 Na linha acima, construimos a thread e iniciamos a sua execução, a partir dai a maquina virtual vai executar o metodo run da thread.
O código colocado dentro do método run nem sempre sera executado por completo, do inicio ao fim. A máquina virtual java pode pausar a execução de uma Thread, para colocar outra Thread no lugar.
As threads tem um atributo chamado prioridade, este atributo influencia em como a máquina virtual vai escolher qual thread deve rodar, por exemplo uma thread com prioridade mínima só vai rodar, quando todas as threads com prioridade máxima estiverem pausadas por algum motivo.
O comportamento para prioridades das threads é diferente de plataforma para plataforma, por isso fique atento ao usar este atributo, pois ele pode ter um comportamento diferente de uma plataforma para outra. A máquina virtual é apenas uma interface para o verdadeiro controlador das threads, que é feito pelo sistema operacional.
Abaixo segue um exemplo de de um código usando Thread:
public class ExemploThread {
   // Mostra uma mensagem de acordo com o nome da thread atual   static void mensagemDaThread(String messagem) {
      String nomeDaThread =
      Thread.currentThread().getName();
      System.out.format("%s: %s%n",nomeDaThread,messagem);
   }
   private static class LoopDaMensagem implements Runnable {
      public void run() {
         String mensagens[] = {
           "Lpoo é a primeira matéria de java"            "Depois vem ALPOO",            "Finalmente quem sobrevive",            "Ai sim pode pegar o diploma"
         };
         try {
            for (int i = 0; i < mensagens.length; i++) {
            // Pause for 4 seconds
               Thread.sleep(4000);
             // Print a message
               mensagemDaThread(mensagens[i]);
            }
         } catch (InterruptedException e) {
            mensagemDaThread("Thread não finalizada!");         }
      }
   }
   public static void main(String args[])throws InterruptedException {
      mensagemDaThread("Iniciando a thread com o loop da mensagem");      long startTime = System.currentTimeMillis();
      Thread[] t = new Thread[5];
      for(int i=0;i<4;i++){
         t[i]= new Thread(new LoopDaMensagem()); 
         t[i].start();
      }
      mensagemDaThread("Esperando a thread terminar");      mensagemDaThread("Finalmente!");
   }
}

E o resultado de rodarmos ele em um ambiente Mac/Os:

main: Iniciando a thread com o loop da mensagem
main: Esperando a thread terminar
main: Finalmente!
main: Iniciando a thread com o loop da mensagem
main: Esperando a thread terminar
main: Finalmente!
Thread-1: Lpoo é a primeira matéria de java
Thread-2: Lpoo é a primeira matéria de java
Thread-4: Lpoo é a primeira matéria de java
Thread-3: Lpoo é a primeira matéria de java
Thread-1: Depois vem ALPOO
Thread-2: Depois vem ALPOO
Thread-4: Depois vem ALPOO
Thread-3: Depois vem ALPOO
Thread-1: Finalmente quem sobrevive
Thread-4: Finalmente quem sobrevive
Thread-3: Finalmente quem sobrevive
Thread-2: Finalmente quem sobrevive
Thread-1: Ai sim pode pegar o diploma
Thread-2: Ai sim pode pegar o diploma
Thread-3: Ai sim pode pegar o diploma
Thread-4: Ai sim pode pegar o diploma
Veja que a sequência de execução das Threads nem sempre é a mesma, e saiba que isso varia de sistema operacional para sistema operacional.

domingo, 21 de abril de 2013

Tratamento de Exceções no Java

Excecoes - Continuacao

Finally

E quando acontece uma excecao em nosso código e tratamos ela, mas mesmo assim ainda temos que fazer algo?

try{
   //Abrimos uma conexao com o banco
   //e vamos salvar um registro, mas e quando acontece uma excecao?
   //ponto 1
} catch(Exception e){
  //Quando a chave do banco e violada, uma exceção será disparada.
  //aqui tratamos a ela.
  //ponto 2

Tudo bem, mas e quando acontece uma exceção, como fica a conexão com o banco de dados?
Bom se fecharmos a conexão no ponto 1, ela nunca sera fechada quando acontecer uma excecao, voce ate pode pensar que a exceção pode ser fechada no ponto 2. 
Ok, mas neste caso, só passamos ali quando acontece uma exceção, e mais para fechar uma conexão com o banco devemos verificar se ela ainda está aberta, neste caso esta lógica ficaria em dois lugares, no ponto 1 e no ponto 2.
Mas e se temos que tratar mais de um tipo de excecao, vamos ter que tentar fechar a conexão com o banco a cada exceção tratada?
Isso nao faz muito sentido certo, bom pra isso e que serve o bloco finally, este bloco vem depois do catch e sempre sera executado, mesmo quando acontecer uma exceção
Aqui é o melhor lugar para colocarmos o código que finaliza a conexão com o banco de dados.

try{
   //Abrimos uma conexao com o banco
   //e vamos salvar um registro, mas e quando acontece uma exceção?
   //ponto 1
} catch(Exception e){
  //Quando a chave do banco e vialoda, uma exceção e disparada.
  //aqui tratamos a exceção.
  //ponto 2
}finally {
  //Aqui fechamos a conexão com o banco, pois esta parte sempre sera executada.
}

Até agora vimos duas maneiras de usar o try em nosso código, a primeira foi usando o try/catch e a segunda usando o try/catch/finally, mas existem três formas de usarmos o try.
A terceira maneira de usar o try é a seguinte:

try{
  //Código
} finally {
 // código que sempre será executado
}

Usando somente o try e o finally a sequência de execução é a mesma, ele executará o código do try, e mesmo quando acontecer uma exceção o código dentro do finally será executado.
O código é executado na mesma sequência que ele é disposto, primeiro o código do try e depois o código do finally.
A sequência de execução do código try é sempre a seguinte, primeiro o código do try é executado, logo após é a vez do código do catch, caso ele esteja presente, logo depois será a vez de executar o código do finally, caso ele esteja presente.

Exercícios:
1) Determine o que acontece com a execução de um código, quando uma exceção é disparada dentro de uma sequência contendo try e finally. Onde esta exceção deve ser tratada? Para onde vai a execução do código depois que uma exceção é disparada?

2) Dado os códigos abaixo, o que acontece quando uma exceção é disparada no ponto 1?


public void metodoExcecao(){
try{
Ponto 1
} finally {
Ponto 2
}
Ponto 3


public void metodoExcecao(){
try{
Ponto 1
} catch(Exception e){
Ponto 2
} finally {
Ponto 3
}

Ponto 4

Qual será a sequencia de execução dos códigos, onde ele passa e onde ele não passa?


segunda-feira, 8 de abril de 2013

Tratamento de erros e Exceções em Java


Exceções e Erros:

Throwable:

Em java temos uma interface chamada Throwable, esta interface é quem determina quais classes podem ser "lançadas" , através do uso da palavra chave throw, no código. Existem duas classes básicas que implementam Throwable, são elas Error (Erro) e Exception (Exceção).

Erros:

Um erro acontece quando há um sério problema com o nosso código, algo que a nossa aplicação não conseguirá tratar, por exemplo:
  • OutOfMemoryError => Erro que acontece quando os nossos programas tentam usar mais memória do que está disponível na máquina virtual
  • StackOverFlowError => acontece quando criamos um loop infinito.
A máquina virtual pode parar de executar por causa dos erros, um exemplo de como conseguir disparar um erro é mostrado a seguir:

//Exemplo de erro de loop infinitopublic boolean stackOverFlow(){   stackOverFlow();   return true;}//Exemplo de erro de estouro de memóriapublic void outOfMemory() {   List lista = new ArrayList();   while(true){      lista.add(new Professor());   }}

Exceções:

Exceções acontecem quando existe alguma coisa de errada no código, ou alguma parte do nosso sistema falha, como o acesso ao banco de dados ficou indisponível (servidor em manutenção), erro de conexão com algum serviço de rede, problemas com a interface de rede, etc.Quando estas falhas acontecem, o nosso sistema não pode parar, ele deve informar ao usuário que aconteceu um erro temporário, e este erro de ver tratado pelo nosso sistema, para mostrar uma mensagem que permita ao usuário entender o que está acontecendo.Toda vez que uma exceção é lançada pelo nosso código, a execução do método/construtor irá parar exatamente onde a exceção foi lançada.
Existem dois tipos de exceções as checáveis e as não checáveis. 


Exceções checáveis:

As exceções checáveis são aquelas onde nós temos que, obrigatoriamente tratá-las, quando um método/construtor as lança, estas exceções extendem Exception.

Exemplo de código com exceção checável:


public void conectaBanco(Connection conexao) throws IllegalArgumentException{
   if(conexao == null){
      throw new IllegalArgumentException("Conexao não pode ser nula");
   }
   //lógica omitida
}

No exemplo acima IllegalArgumentException é uma exceção checável, por isso obrigatoriamente temos que declarar ela na definição do método, usando a palavra throws. Com isso estamos dizendo que este método pode lançar uma exceção deste tipo.
Para acessar o método acima, temos que usar um try catch.

public void acessaConectaBanco() {   try{      conectaBanco(null);   } catch(IllegalArgumentException e) {      e.printStackTrace();   }}

Como IllegalArgumentException é uma exceção checável, obrigatoriamente temos que usar tratá-la no código que chama o método que a dispara e isso é feito através do try catch, que nada mais é do que uma expressão do tipo,
try{   ///tente executar este código}catch (IllegalArgumentException e){   //Caso aconteça uma exceção do tipo illegal argument exception, trate-a aqui.}

Voltando aos conceitos de orientação a objeto, o mesmo código poderia ter sido escrito assim:
public void acessaConectaBanco() {
   try{
      conectaBanco(null);
   } catch(Exception e) {
      e.printStackTrace();
   }
}

já que IllegalArgumentException extende Exception, que é a classe básica des exceções.

Exceções não checáveis:

As exeeções não checáveis, são as exceções que quando lançadas, não precisam ser declaradas no método/construtor, nem tratadas pelo código que executa o método/construtor, estas exceções extendem a classe RunTimeException.
Exceções não checadas:
public void conectaBancoRuntime(Connection conexao){
   if(conexao == null){
      throw new RuntimeException("Conexao nap pose set null");
   }
   //lógica omitida
}
Como estas exceções não precisam ser tratadas, nem declaradas, o mesmo código mostrado acima, poderia ter sido rescrito da seguinte maneira:

public void acessaConectaBancoRuntime() {
   conectaBancoRuntime(null);
}

Neste caso, quando chamarmos este método, a exceção será tratada pela classe que chamou o método acessaConexaoBancoRuntime.
Caso você queira tratar esta exceção lançada, você pode, usando um try catch, da seguinte forma:

public void acessaConectaBancoRuntime() {
   try{
      conectaBancoRuntime(null);
   } catch(RuntimeException e) {
      //tratamento de uma exceção
  }
}

PrintStackTrace:


A interface Throwable define um método chamado printStackTrace(); este método irá imprimir no console toda a lista de métodos que foi usada para chamar o método da exceção, no caso do método acessaConectaBanco, ele irá imprimir o seguinte:

Exception in thread "main" java.lang.IllegalArgumentException: Conexão não pode ser nula
at br.unip.erros.ExemploDeErros.conectaBanco(ExemploDeErros.java:39)
at br.unip.erros.ExemploDeErros.main(ExemploDeErros.java:48)
Acho podemos ver que a exceção aconteceu na thread main (thread principal do programa), foi uma exceção do tipo IllegalArgumentException. Aqui vemos também que a exceção foi lançada no método conectaBanco da classe ExemploDeErros na linha 39.

Exceções quando usar?

Visto os conceitos de exceções, ficamos tentados a extender a classe exception e criar um erro para cada atitude errada dos nossos usuários, ou para cada caso em que um método não retorne algo esperado, por exemplo, em uma tela de login, quando o usuário digita a senha errada, é muito mais fácil criarmos uma exceção SenhaInvalidaException e dispará-la, para depois tratarmos.
Na verdade, muitos dos exemplos encontrados na internet sobre exceções, incentivam este comportamento. A dica para usar é a seguinte:
Quando o seu cliente pode tomar alguma atitude que contornará a exceção, use exceção checada, já se o cliente não poderá fazer nada para contornar a exceção, use uma exceção não checada.
Dentre as principais desvantagens das exceções que podemos citar são:

  • Elas aumentam a quantidade de código escrita
  • Torna o código mais difícil de ler
  • Fragilizam a assinatura dos métodos
  • Não funcionam bem com interfaces
  • São pesadas demais
Carregar a stacktrace para mostrar no método printstacktrace não é uma operação nada fácil, isso requer várias operações na máquina virtual que são muito custosas, portanto a dica é evitar ao máximo.
Exceções devem ser usadas somente quando algo de extraordinário acontece, quando o resultado do método é algo esperado (é esperado que o usuário digite a senha errada, portanto o retorno deve ser algo que indique isso, e não uma exceção). Quando o comportamento do método for realmente algo que não de para tratar, ai sim devemos criar e disparar uma exceção.

Exercícios:

1) Determinar quais das exceções abaixo são do tipo checked, ou unchecked:
java.lang.NullPointerException
java.io.IOException
java.lang.NumberFormatException
java.lang.IllegalArgumentException

2) Altere a classe professor e faça com que ela dispare uma IllegalArgumentException quando ela receba um nome nulo ou vazio, um sobrenome nulo ou vazio.

3) Altere a classe matéria e faça com que ela dispare uma exceção IllegalArgumentException quando ela receba um Curso nulo.

quarta-feira, 3 de abril de 2013

Revisao antes da P1

Arquivo de Revisao para antes da P1:

public class Construtor extends Object {
    private int id; //so sera acessivel dentro desta classe
     protected boolean ativo  = false; // so sera acessivel de dentro desta classe e dos filhos desta classe
     String padrao="Padrao"; // apenas dentro do mesmo pacote, dentro desta classe e dentro dos filhos desta classe

     public String publica = "publica"; // acessado de todos os lugares
     private String nome;
     //Construtor
     public Construtor(String nome, int id){
         this.nome= nome;
         this.id=id;
    }
     //metodo
     public String getNome(){
         return this.nome;
    }

     public int getId(){
         return this.id;
    }

    public static void main(String [] args){
         Construtor cons = new Construtor(" Danilo",1);
        cons.getNome();
        cons.equals("string");
        cons.equals(null);
        " string".equals(cons);
        //{'s','t',...}
    }

     //equals deve retornar obrigatoriamente true // a objetos iguais e false a objetos
    // diferentes
    public boolean equals(Object obj){
        if(this == obj) {
            return true;
        }

         if(obj == null){
             return false;
        }

         if(this.getClass()!=obj.getClass()){
             return false;
        }

         Construtor argumento = (Construtor)obj;

         if(this.getNome()!= argumento.getNome() && this.getNome()!= null && !this.getNome().equals(argumento.getNome)){
             return false;
        }

         if(this.getId()!=argumento.getId()){
             return false;
        }
        return true;
    }

     //retorna obrigatoriamente o mesmo valor inteiro a objetos onde o equals seja verdadeiro,
    // nao necessariamente dois objetos com o mesmo hashcode serao iguais
     public int hashCode(){
         int primo = 7;
         int resultado=1;
         if(this.getNome!=null){
             resultado*=this.getNome().hashCode()*primo;
        }
         if(id!=0){
             resultado*=this.id*primo;
        }

         return resultado;
    }
}


   public class FilhoConstrutor() extends Construtor{
       public FilhoConstrutor(){
          ativo=true;
      }
   }
 }