Java - Multithreading

Java é uma linguagem de programação multithread, o que significa que podemos desenvolver programas multithread usando Java. Um programa multi-threaded contém duas ou mais partes que podem ser executadas simultaneamente e cada parte pode lidar com uma tarefa diferente ao mesmo tempo, fazendo uso otimizado dos recursos disponíveis, especialmente quando seu computador tem várias CPUs.

Por definição, multitarefa é quando vários processos compartilham recursos de processamento comuns, como uma CPU. O multi-threading estende a ideia de multitarefa em aplicativos onde você pode subdividir operações específicas em um único aplicativo em threads individuais. Cada um dos threads pode ser executado em paralelo. O sistema operacional divide o tempo de processamento não apenas entre os diferentes aplicativos, mas também entre cada thread de um aplicativo.

O multi-threading permite que você escreva de uma forma em que várias atividades possam prosseguir simultaneamente no mesmo programa.

Ciclo de Vida de um Tópico

Um segmento passa por vários estágios de seu ciclo de vida. Por exemplo, um encadeamento nasce, é iniciado, executado e, em seguida, morre. O diagrama a seguir mostra o ciclo de vida completo de um encadeamento.

A seguir estão as etapas do ciclo de vida -

  • New- Um novo encadeamento inicia seu ciclo de vida no novo estado. Ele permanece neste estado até que o programa inicie o thread. Também é conhecido comoborn thread.

  • Runnable- Depois que um encadeamento recém-nascido é iniciado, o encadeamento se torna executável. Um thread neste estado é considerado como executando sua tarefa.

  • Waiting- Às vezes, um thread faz a transição para o estado de espera enquanto espera por outro thread para realizar uma tarefa. Um thread faz a transição de volta ao estado executável apenas quando outro thread sinaliza ao thread em espera para continuar em execução.

  • Timed Waiting- Um thread executável pode entrar no estado de espera cronometrado por um intervalo de tempo especificado. Um encadeamento nesse estado faz a transição de volta ao estado executável quando esse intervalo de tempo expira ou quando o evento pelo qual está esperando ocorre.

  • Terminated (Dead) - Um thread executável entra no estado finalizado quando ele conclui sua tarefa ou então termina.

Prioridades de discussão

Cada encadeamento Java tem uma prioridade que ajuda o sistema operacional a determinar a ordem em que os encadeamentos são planejados.

As prioridades de encadeamento Java estão no intervalo entre MIN_PRIORITY (uma constante de 1) e MAX_PRIORITY (uma constante de 10). Por padrão, cada thread tem prioridade NORM_PRIORITY (uma constante de 5).

Threads com prioridade mais alta são mais importantes para um programa e devem ter tempo de processador alocado antes de threads de prioridade mais baixa. No entanto, as prioridades de thread não podem garantir a ordem em que os threads são executados e são muito dependentes da plataforma.

Crie um thread implementando uma interface executável

Se sua classe se destina a ser executada como um thread, você pode conseguir isso implementando um Runnableinterface. Você precisará seguir três etapas básicas -

Passo 1

Como primeira etapa, você precisa implementar um método run () fornecido por um Runnableinterface. Este método fornece um ponto de entrada para o encadeamento e você colocará sua lógica de negócios completa dentro desse método. A seguir está uma sintaxe simples do método run () -

public void run( )

Passo 2

Como uma segunda etapa, você instanciará um Thread objeto usando o seguinte construtor -

Thread(Runnable threadObj, String threadName);

Onde, threadObj é uma instância de uma classe que implementa oRunnable interface e threadName é o nome dado ao novo tópico.

etapa 3

Depois que um objeto Thread é criado, você pode iniciá-lo chamando start(), que executa uma chamada ao método run (). A seguir está uma sintaxe simples do método start () -

void start();

Exemplo

Aqui está um exemplo que cria um novo thread e começa a executá-lo -

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();
   }   
}

Isso produzirá o seguinte resultado -

Resultado

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Criar um thread estendendo uma classe de thread

A segunda maneira de criar um thread é criar uma nova classe que estende Threadclasse usando as duas etapas simples a seguir. Esta abordagem fornece mais flexibilidade no tratamento de vários threads criados usando os métodos disponíveis na classe Thread.

Passo 1

Você precisará substituir run( )método disponível na classe Thread. Este método fornece um ponto de entrada para o encadeamento e você colocará sua lógica de negócios completa dentro desse método. A seguir está uma sintaxe simples do método run () -

public void run( )

Passo 2

Uma vez que o objeto Thread é criado, você pode iniciá-lo chamando start(), que executa uma chamada ao método run (). A seguir está uma sintaxe simples do método start () -

void start( );

Exemplo

Aqui está o programa anterior reescrito para estender o Thread -

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

Isso produzirá o seguinte resultado -

Resultado

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Métodos de linha

A seguir está a lista de métodos importantes disponíveis na classe Thread.

Sr. Não. Método e Descrição
1

public void start()

Inicia o thread em um caminho de execução separado e, a seguir, invoca o método run () neste objeto Thread.

2

public void run()

Se este objeto Thread foi instanciado usando um destino Runnable separado, o método run () é invocado nesse objeto Runnable.

3

public final void setName(String name)

Altera o nome do objeto Thread. Também existe um método getName () para recuperar o nome.

4

public final void setPriority(int priority)

Define a prioridade deste objeto Thread. Os valores possíveis estão entre 1 e 10.

5

public final void setDaemon(boolean on)

Um parâmetro true denota este Thread como um thread daemon.

6

public final void join(long millisec)

O encadeamento atual invoca este método em um segundo encadeamento, fazendo com que o encadeamento atual seja bloqueado até que o segundo encadeamento termine ou o número especificado de milissegundos passe.

7

public void interrupt()

Interrompe este thread, fazendo com que ele continue a execução se ele foi bloqueado por qualquer motivo.

8

public final boolean isAlive()

Retorna verdadeiro se o encadeamento estiver ativo, o que ocorre a qualquer momento após o encadeamento ter sido iniciado, mas antes de sua execução até a conclusão.

Os métodos anteriores são invocados em um objeto Thread específico. Os métodos a seguir na classe Thread são estáticos. Chamar um dos métodos estáticos executa a operação no thread em execução no momento.

Sr. Não. Método e Descrição
1

public static void yield()

Faz com que o encadeamento atualmente em execução ceda a qualquer outro encadeamento com a mesma prioridade que esteja aguardando para ser agendado.

2

public static void sleep(long millisec)

Faz com que o thread em execução no momento seja bloqueado por pelo menos o número especificado de milissegundos.

3

public static boolean holdsLock(Object x)

Retorna verdadeiro se a thread atual mantém o bloqueio no objeto fornecido.

4

public static Thread currentThread()

Retorna uma referência ao encadeamento atualmente em execução, que é o encadeamento que invoca este método.

5

public static void dumpStack()

Imprime o rastreamento de pilha para o encadeamento atualmente em execução, o que é útil ao depurar um aplicativo multithread.

Exemplo

O programa ThreadClassDemo a seguir demonstra alguns desses métodos da classe Thread. Considere uma aulaDisplayMessage que implementa Runnable -

// File Name : DisplayMessage.java
// Create a thread to implement Runnable

public class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
      this.message = message;
   }
   
   public void run() {
      while(true) {
         System.out.println(message);
      }
   }
}

A seguir está outra classe que estende a classe Thread -

// File Name : GuessANumber.java
// Create a thread to extentd Thread

public class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
      this.number = number;
   }
   
   public void run() {
      int counter = 0;
      int guess = 0;
      do {
         guess = (int) (Math.random() * 100 + 1);
         System.out.println(this.getName() + " guesses " + guess);
         counter++;
      } while(guess != number);
      System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}

A seguir está o programa principal, que faz uso das classes definidas acima -

// File Name : ThreadClassDemo.java
public class ThreadClassDemo {

   public static void main(String [] args) {
      Runnable hello = new DisplayMessage("Hello");
      Thread thread1 = new Thread(hello);
      thread1.setDaemon(true);
      thread1.setName("hello");
      System.out.println("Starting hello thread...");
      thread1.start();
      
      Runnable bye = new DisplayMessage("Goodbye");
      Thread thread2 = new Thread(bye);
      thread2.setPriority(Thread.MIN_PRIORITY);
      thread2.setDaemon(true);
      System.out.println("Starting goodbye thread...");
      thread2.start();

      System.out.println("Starting thread3...");
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try {
         thread3.join();
      } catch (InterruptedException e) {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread4...");
      Thread thread4 = new GuessANumber(75);
      
      thread4.start();
      System.out.println("main() is ending...");
   }
}

Isso produzirá o seguinte resultado. Você pode tentar este exemplo repetidamente e obterá um resultado diferente a cada vez.

Resultado

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

Principais conceitos de multithreading Java

Ao fazer a programação multithreading em Java, você precisa ter os seguintes conceitos muito úteis -