Java oferece vários recursos para trabalhar com programação concorrente, embora não seja uma linguagem vastamente utilizada para este fim. A partir da versão 5 do Java esta incluso uma API para programação concorrente.

Threads

A principal abstração para programação concorrente são as Threads. Thread são seqüências de instrução que podem ser executadas em simultaneamente com outras threads. As threads vão compartilhar entre si recursos e memória de maneira direta.

A mais de uma forma de criar uma Thread, a forma que eu gosto mais é implementando a interface Runnable. Um Olá Mundo para threads usando a interface Runnable:

public class Ola implements Runnable{
   public void run(){
      System.out.println("Eu sou uma thread!");
   }

   public static void main(String args[]){
      Runnable r_ola = new Ola();
      Thread t_ola = new Thread(r_ola);
      t_ola.start();
   }
}

Compilando e executando temos:

$ javac Ola.java
$java Ola
Eu sou uma thread!

Criar Threads implementando o método Runnable é melhor porque você pode fazer com que a classe Ola seja filha de uma outra classe qualquer, se houver necessidade.

O start, dado na linha 9, vai inicializar aquela thread e passar o controle dela para o escalonador de threads. Você não tem garantias sobre para qual processador ela vai, nem quando ela vai ser executada. Se você enviar várias threads, você não saberá em que ordem elas vão ser executadas. O que importa é que elas serão executadas.

O comando start não bloqueia o fluxo de seu código, ou seja, você dá o start e continua, o programa não vai ficar ali parado esperando a thread ser executada.

A outra maneira de se criar uma Thread é criando uma classe diretamente da classe Thread.

public class OutroOla extends Thread{
   public void run(){
      System.out.println("Eu sou uma Thread!");
   }
   public static void main(String args[]){
      Thread t_ola = new OutroOla();
      t_ola.start();
   }
}

Da mesma maneira:

$ javac OutroOla.java
$java OutroOla
Eu sou uma Thread!

Criar uma classe que seja filha direta da Thread pode ser inconveniente em vários casos, pois você pode querer que sua classe seja herdeira de alguma outra classe e o Java não suporta herança múltipla (engula o choro).

Usar a interface Runnable é deixa sua classe mais geral, mas em alguns casos é conveniente usar a Thread.

Somatório

Somatório

Vamos fazer aquele exemplo do somatório usando Threads. O primeira passo é identificar que trecho do código pode ser paralelizado.
Dois trechos de código podem ser paralelizados quando eles não tem uma dependência funcional entre si, ou seja, um não tem que esperar o outro terminar para executar.

Dê uma olhada no somatório mais de perto e tente encontrar o que está mais paralelizável:

Somatório detalhado

O que está mais paralelizável aí são os termos do somatório, que estão na forma i². Elevar um número ao quadrado não envolve olhar o quadrado de algum outro número. Já a soma, envolve olhar o resultado de pelo menos dois quadrados.

O que paralelizar pode ser algo trivial mas as vezes é algo sutil. Em geral, tente paralelizar os laços.

Somatório usando Threads

O que vamos fazer é o seguinte, vamos criar uma thread para cada termo do somatório. São 6 termos, então são 6 threads.

Para fazer a soma, nos vamos esperar todas as threads executarem. O método join aguarda que a thread termine, ou seja, que o método run chegue ao fim.

Eis o código:

public class ThreadQuadrado extends Thread{
   int i;
   ThreadQuadrado(int i){
      super();
      this.i = i;
   }

   public void run(){
      this.i = i*i;
   }

   public static void main(String args[]){
	  ThreadQuadrado termo[] = new ThreadQuadrado[6];

	  // Lançamos 5 threads
	  for(int j=0;j< =5;j++){
		  termo[j] = new ThreadQuadrado(j);
		  termo[j].start();
	  }

	  // Esperamos 5 threads acabarem e somamos os resultados
	  int soma = 0;
	  for(int j=0;j<=5;j++)
		  try {
			  termo[j].join();
			  soma = soma + termo[j].i;
		  } catch (InterruptedException e) {}

      System.out.println(soma);
   }
}

A classe Thread quadrado e inicializada (linha 3) com um inteiro. O trabalho dela é elevar esse inteiro ao quadrado. Ela vai fazer isso no método run (linha 8) e guardar o resultado na sua variável i.

O método main não tem nada a ver com a thread em si, ele só está aí pela comodidade de não criar uma outra classe.

No main vamos criar um vetor de 6 threads (linha 13). No primeiro laço (linha 16) vamos instanciar e lançar cada thread.

No segundo laço vamos, para cada thread, esperar que ela acabe através do método join. Depois que uma thread acaba, vamos pegar o valor i dela e soma-lo na variável soma.

O método join nos obriga a envolve-lo por um try catch.

Compilando e executando temos:

$ javac ThreadQuadrado.java
$ java ThreadQuadrado
55

Isso é só o mais básico sobre threads. As partes mais interessante estão nas estruturas do pacote java.util.concurrent.
Se você quer continuar seu estudo a partir daqui, eu recomendo que você estude semáforos.

Voltar ao artigo principal

Links Úteis: