Programação concorrente em Java - Threads


A linguagem Java basicamente utiliza a Interface Runnable e a classe Thread para possibilitar a execução de mais de um fluxo de controle sobre o código de programa. Aqui temos um exemplo simples para ilustrar um dos métodos de criação de múltiplos fluxos de execução (o outro método consiste em estender a classe Thread).

A metáfora é que as instâncias da classe Thread são abstrações de trabalhadores:
Thread operario= new Thread();
Thread trabalhador= new Thread();

Mas trabalhadores precisam de tarefas. As instâncias de classes que implementam a interface Runnable devem definir as tarefas.
class trabalho implements Runnable{...}
class tarefa implements Runnable{...}

Podemos associar uma tarefa a um trabalhador utilizando um construtor de Thread. Uma instância de Thread invoca o método run() da instância de Runnable. o método start() de uma instância de Thread inicia o fluxo de controle. Só  se pode chamar start() uma vez. Resumindo:
class Tarefa implements Runnable{
... public void run() {/* código para realizar a tarefa */}
...
}

...
  public static void main(String[] args){

    Runnable tarefa=new Tarefa();
    Thread trabalhador=new Thread(tarefa);
    trabalhador.start(); /* o fluxo de controle irá retornar, mas um novo fluxo permanecerá em start(). */


No programa abaixo é utilizada a uma interface padrão denominada Runnable. Uma classe que implementa a interface Runnable deve implementar um método com a seguinte assinatura: public void run(). O programa utiliza ainda uma classe denominada Thread. Um dos construtores da classe Thread recebe como parâmetro uma referência para um objeto cuja classe implementa a interface Runnable. Quando invocamos o método start() de uma instância de Thread o fluxo de controle é duplicado e um dos fluxos de controle segue pelo método run() do objeto Runnable associado e o outro fluxo de controle retorna para o ponto onde o método start() foi invocado. A menos dos aspectos denominados de sincronização, não existem garantias sobre a velocidade relativa de execução dos fluxos de controle. Enquanto existirem fluxos de controle a execução de um programa Java continua, ou seja, o fluxo de controle que percorre o método main pode terminar e o programa continua em execução desde que exista um ou mais fluxos de controle. No programa abaixo após o término do fluxo de controle sobre o método main permanecerão dois fluxos de controle sobre as duas instâncias de Thread correspondentes a duas intâncias de Tarefa. Descreva de maneira concisa o que é escrito pelo programa abaixo.
 

class Tarefa implements Runnable{
  private String s;
  Tarefa(String id){
    s=id;
  }
  public void run(){
    for(;;) System.out.print(s+" ");
  }
}
class linhaSimples{
  public static void main(String[] args){
    Runnable tarefa1=new Tarefa("1");
    Runnable tarefa2=new Tarefa("2");
    Thread trabalhador1=new Thread(tarefa1);
    Thread trabalhador2=new Thread(tarefa2);
    trabalhador1.start();
    trabalhador2.start();
  }
}