Simultaneidade Java - Guia Rápido

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.

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

Ciclo de vida de um fio

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

A seguir estão os estágios 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 neste estado faz a transição de volta ao estado executável quando esse intervalo de tempo expira ou quando ocorre o evento pelo qual está esperando.

  • Terminated (Dead) - Um thread executável entra no estado finalizado quando completa 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 do 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 segmento, 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();

Example

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 -

Output

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( );

Example

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 -

Output

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.

Neste capítulo, discutiremos os diferentes aspectos da configuração de um ambiente adequado para Java.

Configuração de ambiente local

Se você ainda deseja configurar seu ambiente para a linguagem de programação Java, esta seção o orienta sobre como fazer download e configurar o Java em sua máquina. A seguir estão as etapas para configurar o ambiente.

Java SE está disponível gratuitamente no link Download Java . Você pode baixar uma versão com base em seu sistema operacional.

Siga as instruções para baixar o Java e executar o .exepara instalar o Java em sua máquina. Depois de instalar o Java em sua máquina, você precisará definir as variáveis ​​de ambiente para apontar para os diretórios de instalação corretos -

Configurando o caminho para o Windows

Supondo que você tenha instalado o Java no diretório c: \ Arquivos de programas \ java \ jdk -

  • Clique com o botão direito em 'Meu Computador' e selecione 'Propriedades'.

  • Clique no botão 'Variáveis ​​de ambiente' na guia 'Avançado'.

  • Agora, altere a variável 'Path' para que também contenha o caminho para o executável Java. Exemplo, se o caminho está definido atualmente para 'C: \ WINDOWS \ SYSTEM32', altere seu caminho para 'C: \ WINDOWS \ SYSTEM32; c: \ Arquivos de programas \ java \ jdk \ bin'.

Configurando o caminho para Linux, UNIX, Solaris, FreeBSD

A variável de ambiente PATH deve ser definida para apontar para onde os binários Java foram instalados. Consulte a documentação do shell, se tiver problemas para fazer isso.

Por exemplo, se você usar bash como seu shell, você adicionaria a seguinte linha ao final de seu '.bashrc: export PATH = / path / to / java: $ PATH'

Editores Java populares

Para escrever seus programas Java, você precisará de um editor de texto. Existem IDEs ainda mais sofisticados disponíveis no mercado. Mas, por agora, você pode considerar um dos seguintes -

  • Notepad - Na máquina Windows, você pode usar qualquer editor de texto simples como o Notepad (recomendado para este tutorial), TextPad.

  • Netbeans - Um IDE Java que é de código aberto e gratuito que pode ser baixado de https://netbeans.org/index.html.

  • Eclipse - Um IDE Java desenvolvido pela comunidade de código aberto eclipse e pode ser baixado de https://www.eclipse.org/.

Core Java fornece controle completo sobre o programa multithread. Você pode desenvolver um programa multithread que pode ser suspenso, retomado ou interrompido completamente com base em seus requisitos. Existem vários métodos estáticos que você pode usar em objetos de thread para controlar seu comportamento. A tabela a seguir lista esses métodos -

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

public void suspend()

Este método coloca um thread no estado suspenso e pode ser retomado usando o método resume ().

2

public void stop()

Este método interrompe um thread completamente.

3

public void resume()

Este método retoma uma thread, que foi suspensa usando o método suspend ().

4

public void wait()

Faz com que a thread atual espere até que outra thread invoque o notificar ().

5

public void notify()

Acorda um único thread que está aguardando no monitor deste objeto.

Esteja ciente de que as versões mais recentes do Java descontinuaram o uso dos métodos suspend (), resume () e stop () e, portanto, você precisa usar as alternativas disponíveis.

Exemplo

class RunnableDemo implements Runnable {
   public Thread t;
   private String threadName;
   boolean suspended = false;

   RunnableDemo(String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );

      try {
         
         for(int i = 10; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);

            // Let the thread sleep for a while.
            Thread.sleep(300);

            synchronized(this) {
               
               while(suspended) {
                  wait();
               }
            }
         }
      } 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 ();
      }
   }
   
   void suspend() {
      suspended = true;
   }
   
   synchronized void resume() {
      suspended = false;
      notify();
   }
}

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();

      try {
         Thread.sleep(1000);
         R1.suspend();
         System.out.println("Suspending First Thread");
         Thread.sleep(1000);
         R1.resume();
         System.out.println("Resuming First Thread");
         
         R2.suspend();
         System.out.println("Suspending thread Two");
         Thread.sleep(1000);
         R2.resume();
         System.out.println("Resuming thread Two");
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      } try {
         System.out.println("Waiting for threads to finish.");
         R1.t.join();
         R2.t.join();
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }
      System.out.println("Main thread exiting.");
   }
}

O programa acima produz a seguinte saída -

Resultado

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.

Se você está ciente da comunicação entre processos, será fácil entender a comunicação entre processos. A comunicação entre threads é importante quando você desenvolve um aplicativo em que dois ou mais threads trocam algumas informações.

Existem três métodos simples e um pequeno truque que torna possível a comunicação de thread. Todos os três métodos estão listados abaixo -

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

public void wait()

Faz com que a thread atual espere até que outra thread invoque o notificar ().

2

public void notify()

Acorda um único thread que está aguardando no monitor deste objeto.

3

public void notifyAll()

Desperta todos os threads que chamaram wait () no mesmo objeto.

Esses métodos foram implementados como finalmétodos em Object, portanto, estão disponíveis em todas as classes. Todos os três métodos podem ser chamados apenas de dentro de umsynchronized contexto.

Exemplo

Este exemplo mostra como dois threads podem se comunicar usando wait() e notify()método. Você pode criar um sistema complexo usando o mesmo conceito.

class Chat {
   boolean flag = false;

   public synchronized void Question(String msg) {

      if (flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = true;
      notify();
   }

   public synchronized void Answer(String msg) {

      if (!flag) {
         
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = false;
      notify();
   }
}

class T1 implements Runnable {
   Chat m;
   String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };

   public T1(Chat m1) {
      this.m = m1;
      new Thread(this, "Question").start();
   }

   public void run() {
   
      for (int i = 0; i < s1.length; i++) {
         m.Question(s1[i]);
      }
   }
}

class T2 implements Runnable {
   Chat m;
   String[] s2 = { "Hi", "I am good, what about you?", "Great!" };

   public T2(Chat m2) {
      this.m = m2;
      new Thread(this, "Answer").start();
   }

   public void run() {

      for (int i = 0; i < s2.length; i++) {
         m.Answer(s2[i]);
      }
   }
}

public class TestThread {

   public static void main(String[] args) {
      Chat m = new Chat();
      new T1(m);
      new T2(m);
   }
}

Quando o programa acima é cumprido e executado, ele produz o seguinte resultado -

Resultado

Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!

O exemplo acima foi obtido e modificado de [https://stackoverflow.com/questions/2170520/inter-thread-communication-in-java]

Exemplo de multithreading com sincronização

Aqui está o mesmo exemplo que imprime o valor do contador em sequência e sempre que o executamos, ele produz o mesmo resultado.

Exemplo

class PrintDemo {
   
   public void printCount() {
      
      try {
         
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo(String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      
      synchronized(PD) {
         PD.printCount();
      }
      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[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo T2 = new ThreadDemo("Thread - 2 ", PD);

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Isso produz o mesmo resultado toda vez que você executa este programa -

Resultado

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Deadlock descreve uma situação em que dois ou mais threads são bloqueados para sempre, esperando um pelo outro. O deadlock ocorre quando vários threads precisam dos mesmos bloqueios, mas os obtêm em ordem diferente. Um programa Java multithread pode sofrer com a condição de deadlock porque osynchronizedpalavra-chave faz com que o encadeamento em execução bloqueie enquanto aguarda o bloqueio, ou monitor, associado ao objeto especificado. Aqui está um exemplo.

Exemplo

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
   
      public void run() {
      
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }

   private static class ThreadDemo2 extends Thread {
   
      public void run() {
      
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Quando você compila e executa o programa acima, você encontra uma situação de impasse e a seguir está a saída produzida pelo programa -

Resultado

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

O programa acima irá travar para sempre porque nenhum dos threads está em posição de prosseguir e aguardando um ao outro para liberar o bloqueio, então você pode sair do programa pressionando CTRL + C.

Exemplo de solução de deadlock

Vamos mudar a ordem de bloqueio e execução do mesmo programa para ver se ambos os threads ainda esperam um pelo outro -

Exemplo

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
   
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }

   private static class ThreadDemo2 extends Thread {
      
      public void run() {
         
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");
           
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");

            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

Portanto, apenas alterar a ordem dos bloqueios evita que o programa entre em uma situação de impasse e conclui com o seguinte resultado -

Resultado

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

O exemplo acima é apenas para deixar o conceito claro, no entanto, é um conceito complexo e você deve mergulhar fundo nele antes de desenvolver seus aplicativos para lidar com situações de deadlock.

A classe ThreadLocal é usada para criar variáveis ​​locais de thread que só podem ser lidas e escritas pela mesma thread. Por exemplo, se dois encadeamentos estão acessando o código com referência à mesma variável threadLocal, cada encadeamento não verá nenhuma modificação na variável threadLocal feita por outro encadeamento.

Métodos ThreadLocal

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

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

public T get()

Retorna o valor na cópia do segmento atual desta variável local do segmento.

2

protected T initialValue()

Retorna o "valor inicial" do segmento atual para esta variável local do segmento.

3

public void remove()

Remove o valor do segmento atual para esta variável local do segmento.

4

public void set(T value)

Define a cópia do segmento atual desta variável local do segmento para o valor especificado.

Exemplo

O programa TestThread a seguir demonstra alguns desses métodos da classe ThreadLocal. Aqui usamos duas variáveis ​​de contador, uma é a variável normal e outra é ThreadLocal.

class RunnableDemo implements Runnable {
   int counter;
   ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>();

   public void run() {     
      counter++;

      if(threadLocalCounter.get() != null) {
         threadLocalCounter.set(threadLocalCounter.get().intValue() + 1);
      } else {
         threadLocalCounter.set(0);
      }
      System.out.println("Counter: " + counter);
      System.out.println("threadLocalCounter: " + threadLocalCounter.get());
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo commonInstance = new RunnableDemo();

      Thread t1 = new Thread(commonInstance);
      Thread t2 = new Thread(commonInstance);
      Thread t3 = new Thread(commonInstance);
      Thread t4 = new Thread(commonInstance);

      t1.start();
      t2.start();
      t3.start();
      t4.start();

      // wait for threads to end
      try {
         t1.join();
         t2.join();
         t3.join();
         t4.join();
      } catch (Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Counter: 1
threadLocalCounter: 0
Counter: 2
threadLocalCounter: 0
Counter: 3
threadLocalCounter: 0
Counter: 4
threadLocalCounter: 0

Você pode ver que o valor de counter é aumentado em cada thread, mas threadLocalCounter permanece 0 para cada thread.

Um java.util.concurrent.ThreadLocalRandom é uma classe de utilitário introduzida a partir do jdk 1.7 em diante e é útil quando vários threads ou ForkJoinTasks são necessários para gerar números aleatórios. Ele melhora o desempenho e tem menos contenção do que o método Math.random ().

Métodos ThreadLocalRandom

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

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

public static ThreadLocalRandom current()

Retorna o ThreadLocalRandom do segmento atual.

2

protected int next(int bits)

Gera o próximo número pseudoaleatório.

3

public double nextDouble(double n)

Retorna um valor duplo pseudoaleatório uniformemente distribuído entre 0 (inclusivo) e o valor especificado (exclusivo).

4

public double nextDouble(double least, double bound)

Retorna um valor pseudoaleatório distribuído uniformemente entre o valor mínimo fornecido (inclusivo) e o limite (exclusivo).

5

public int nextInt(int least, int bound)

Retorna um valor pseudoaleatório distribuído uniformemente entre o valor mínimo fornecido (inclusivo) e o limite (exclusivo).

6

public long nextLong(long n)

Retorna um valor pseudoaleatório distribuído uniformemente entre 0 (inclusivo) e o valor especificado (exclusivo).

7

public long nextLong(long least, long bound)

Retorna um valor pseudoaleatório distribuído uniformemente entre o valor mínimo fornecido (inclusivo) e o limite (exclusivo).

8

public void setSeed(long seed)

Lança UnsupportedOperationException.

Exemplo

O programa TestThread a seguir demonstra alguns desses métodos da interface Lock. Aqui, usamos lock () para obter o bloqueio e desbloquear () para liberar o bloqueio.

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ThreadLocalRandom;

public class TestThread {
  
   public static void main(final String[] arguments) {
      System.out.println("Random Integer: " + new Random().nextInt());  
      System.out.println("Seeded Random Integer: " + new Random(15).nextInt());  
      System.out.println(
         "Thread Local Random Integer: " + ThreadLocalRandom.current().nextInt());
      
      final ThreadLocalRandom random = ThreadLocalRandom.current();  
      random.setSeed(15); //exception will come as seeding is not allowed in ThreadLocalRandom.
      System.out.println("Seeded Thread Local Random Integer: " + random.nextInt());  
   }
}

Isso produzirá o seguinte resultado.

Resultado

Random Integer: 1566889198
Seeded Random Integer: -1159716814
Thread Local Random Integer: 358693993
Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.concurrent.ThreadLocalRandom.setSeed(Unknown Source)
        at TestThread.main(TestThread.java:21)

Aqui, usamos as classes ThreadLocalRandom e Random para obter números aleatórios.

Uma interface java.util.concurrent.locks.Lock é usada como mecanismo de sincronização de thread semelhante aos blocos sincronizados. O novo mecanismo de travamento é mais flexível e oferece mais opções do que um bloco sincronizado. As principais diferenças entre um bloqueio e um bloco sincronizado são as seguintes -

  • Guarantee of sequence- O bloco sincronizado não oferece nenhuma garantia de sequência na qual o thread em espera terá acesso. A interface de bloqueio cuida disso.

  • No timeout- O bloco sincronizado não tem opção de tempo limite se o bloqueio não for concedido. A interface de bloqueio fornece essa opção.

  • Single method - O bloco sincronizado deve estar totalmente contido em um único método, enquanto os métodos lock () e unlock () de uma interface de bloqueio podem ser chamados em métodos diferentes.

Métodos de bloqueio

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

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

public void lock()

Adquire a fechadura.

2

public void lockInterruptibly()

Adquire o bloqueio, a menos que o segmento atual seja interrompido.

3

public Condition newCondition()

Retorna uma nova instância de Condition que está associada a esta instância de Lock.

4

public boolean tryLock()

Adquire o bloqueio apenas se estiver livre no momento da chamada.

5

public boolean tryLock()

Adquire o bloqueio apenas se estiver livre no momento da chamada.

6

public boolean tryLock(long time, TimeUnit unit)

Adquire o bloqueio se ele estiver livre dentro do tempo de espera determinado e o segmento atual não tiver sido interrompido.

7

public void unlock()

Libera o bloqueio.

Exemplo

O programa TestThread a seguir demonstra alguns desses métodos da interface Lock. Aqui, usamos lock () para obter o bloqueio e desbloquear () para liberar o bloqueio.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class PrintDemo {
   private final Lock queueLock = new ReentrantLock();

   public void print() {
      queueLock.lock();

      try {
         Long duration = (long) (Math.random() * 10000);
         System.out.println(Thread.currentThread().getName() 
            + "  Time Taken " + (duration / 1000) + " seconds.");
         Thread.sleep(duration);
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         System.out.printf(
            "%s printed the document successfully.\n", Thread.currentThread().getName());
         queueLock.unlock();
      }
   }
}

class ThreadDemo extends Thread {
   PrintDemo  printDemo;

   ThreadDemo(String name,  PrintDemo printDemo) {
      super(name);
      this.printDemo = printDemo;
   }   

   @Override
   public void run() {
      System.out.printf(
         "%s starts printing a document\n", Thread.currentThread().getName());
      printDemo.print();
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo t1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo t2 = new ThreadDemo("Thread - 2 ", PD);
      ThreadDemo t3 = new ThreadDemo("Thread - 3 ", PD);
      ThreadDemo t4 = new ThreadDemo("Thread - 4 ", PD);

      t1.start();
      t2.start();
      t3.start();
      t4.start();
   }
}

Isso produzirá o seguinte resultado.

Resultado

Thread - 1  starts printing a document
Thread - 4  starts printing a document
Thread - 3  starts printing a document
Thread - 2  starts printing a document
Thread - 1   Time Taken 4 seconds.
Thread - 1  printed the document successfully.
Thread - 4   Time Taken 3 seconds.
Thread - 4  printed the document successfully.
Thread - 3   Time Taken 5 seconds.
Thread - 3  printed the document successfully.
Thread - 2   Time Taken 4 seconds.
Thread - 2  printed the document successfully.

Usamos a classe ReentrantLock como uma implementação da interface Lock aqui. A classe ReentrantLock permite que um thread bloqueie um método, mesmo se já tiver o bloqueio em outro método.

Uma interface java.util.concurrent.locks.ReadWriteLock permite que vários threads sejam lidos por vez, mas apenas um thread pode gravar por vez.

  • Read Lock - Se nenhuma thread bloqueou ReadWriteLock para gravação, então, várias threads podem acessar o bloqueio de leitura.

  • Write Lock - Se nenhum thread estiver lendo ou gravando, então um thread pode acessar o bloqueio de gravação.

Métodos de bloqueio

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

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

public Lock readLock()

Retorna o bloqueio usado para leitura.

2

public Lock writeLock()

Retorna o bloqueio usado para escrever.

Exemplo

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestThread {
   private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
   private static String message = "a";

   public static void main(String[] args) throws InterruptedException {
      Thread t1 = new Thread(new WriterA());
      t1.setName("Writer A");
      
      Thread t2 = new Thread(new WriterB());
      t2.setName("Writer B");
      
      Thread t3 = new Thread(new Reader());
      t3.setName("Reader");
      t1.start();
      t2.start();
      t3.start();
      t1.join();
      t2.join();
      t3.join();
   }

   static class Reader implements Runnable {

      public void run() {
         
         if(lock.isWriteLocked()) {
            System.out.println("Write Lock Present.");
         }
         lock.readLock().lock();

         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            System.out.println(Thread.currentThread().getName() +": "+ message );
            lock.readLock().unlock();
         }
      }
   }

   static class WriterA implements Runnable {

      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("a");
            lock.writeLock().unlock();
         }
      }
   }

   static class WriterB implements Runnable {

      public void run() {
         lock.writeLock().lock();
         
         try {
            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() 
               + "  Time Taken " + (duration / 1000) + " seconds.");
            Thread.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         } finally {
            message = message.concat("b");
            lock.writeLock().unlock();
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Writer A  Time Taken 6 seconds.
Write Lock Present.
Writer B  Time Taken 2 seconds.
Reader  Time Taken 0 seconds.
Reader: aab

Uma interface java.util.concurrent.locks.Condition fornece uma capacidade de thread para suspender sua execução, até que a condição fornecida seja verdadeira. Um objeto Condition está necessariamente vinculado a um Lock e deve ser obtido usando o método newCondition ().

Métodos de Condição

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

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

public void await()

Faz com que o thread atual espere até ser sinalizado ou interrompido.

2

public boolean await(long time, TimeUnit unit)

Faz com que o thread atual espere até ser sinalizado ou interrompido, ou até que o tempo de espera especificado passe.

3

public long awaitNanos(long nanosTimeout)

Faz com que o thread atual espere até ser sinalizado ou interrompido, ou até que o tempo de espera especificado passe.

4

public long awaitUninterruptibly()

Faz com que o segmento atual espere até ser sinalizado.

5

public long awaitUntil()

Faz com que o encadeamento atual espere até ser sinalizado ou interrompido, ou até que o prazo especificado expire.

6

public void signal()

Acorda um fio de espera.

7

public void signalAll()

Desperta todos os tópicos em espera.

Exemplo

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestThread {

   public static void main(String[] args) throws InterruptedException {
      ItemQueue itemQueue = new ItemQueue(10);

      //Create a producer and a consumer.
      Thread producer = new Producer(itemQueue);
      Thread consumer = new Consumer(itemQueue);

      //Start both threads.
      producer.start();
      consumer.start();

      //Wait for both threads to terminate.
      producer.join();
      consumer.join();
   }

   static class ItemQueue {
      private Object[] items = null;
      private int current = 0;
      private int placeIndex = 0;
      private int removeIndex = 0;

      private final Lock lock;
      private final Condition isEmpty;
      private final Condition isFull;

      public ItemQueue(int capacity) {
         this.items = new Object[capacity];
         lock = new ReentrantLock();
         isEmpty = lock.newCondition();
         isFull = lock.newCondition();
      }

      public void add(Object item) throws InterruptedException {
         lock.lock();

         while(current >= items.length)
            isFull.await();

         items[placeIndex] = item;
         placeIndex = (placeIndex + 1) % items.length;
         ++current;

         //Notify the consumer that there is data available.
         isEmpty.signal();
         lock.unlock();
      }

      public Object remove() throws InterruptedException {
         Object item = null;

         lock.lock();

         while(current <= 0) {
            isEmpty.await();
         }
         item = items[removeIndex];
         removeIndex = (removeIndex + 1) % items.length;
         --current;

         //Notify the producer that there is space available.
         isFull.signal();
         lock.unlock();

         return item;
      }

      public boolean isEmpty() {
         return (items.length == 0);
      }
   }

   static class Producer extends Thread {
      private final ItemQueue queue;
      
      public Producer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         String[] numbers =
            {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};

         try {
            
            for(String number: numbers) {
               System.out.println("[Producer]: " + number);
            }
            queue.add(null);
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         } 
      }
   }

   static class Consumer extends Thread {
      private final ItemQueue queue;
      
      public Consumer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         
         try {
            
            do {
               Object number = queue.remove();
               System.out.println("[Consumer]: " + number);

               if(number == null) {
                  return;
               }
            } while(!queue.isEmpty());
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

[Producer]: 1
[Producer]: 2
[Producer]: 3
[Producer]: 4
[Producer]: 5
[Producer]: 6
[Producer]: 7
[Producer]: 8
[Producer]: 9
[Producer]: 10
[Producer]: 11
[Producer]: 12
[Consumer]: null

Uma classe java.util.concurrent.atomic.AtomicInteger fornece operações no valor int subjacente que pode ser lido e gravado atomicamente e também contém operações atômicas avançadas. AtomicInteger oferece suporte a operações atômicas na variável int subjacente. Ele possui métodos get e set que funcionam como leituras e gravações em variáveis ​​voláteis. Ou seja, um conjunto tem um relacionamento acontece antes com qualquer obtenção subsequente na mesma variável. O método atômico compareAndSet também possui esses recursos de consistência de memória.

Métodos AtomicInteger

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

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

public int addAndGet(int delta)

Adiciona atomicamente o valor fornecido ao valor atual.

2

public boolean compareAndSet(int expect, int update)

Define atomicamente o valor para o valor atualizado fornecido se o valor atual for igual ao valor esperado.

3

public int decrementAndGet()

Diminui atomicamente em um o valor atual.

4

public double doubleValue()

Retorna o valor do número especificado como um duplo.

5

public float floatValue()

Retorna o valor do número especificado como um float.

6

public int get()

Obtém o valor atual.

7

public int getAndAdd(int delta)

Atomicamente adiciona o valor fornecido ao valor atual.

8

public int getAndDecrement()

Diminui atomicamente em um o valor atual.

9

public int getAndIncrement()

Aumenta atomicamente em um o valor atual.

10

public int getAndSet(int newValue)

Define atomicamente o valor fornecido e retorna o valor antigo.

11

public int incrementAndGet()

Aumenta atomicamente em um o valor atual.

12

public int intValue()

Retorna o valor do número especificado como um inteiro.

13

public void lazySet(int newValue)

Eventualmente, define o valor fornecido.

14

public long longValue()

Retorna o valor do número especificado como um longo.

15

public void set(int newValue)

Define o valor fornecido.

16

public String toString()

Retorna a representação de String do valor atual.

17

public boolean weakCompareAndSet(int expect, int update)

Define atomicamente o valor para o valor atualizado fornecido se o valor atual for igual ao valor esperado.

Exemplo

O seguinte programa TestThread mostra uma implementação não segura de contador em ambiente baseado em thread.

public class TestThread {

   static class Counter {
      private int c = 0;

      public void increment() {
         c++;
      }

      public int value() {
         return c;
      }
   }
   
   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {
         
         new Thread(new Runnable() {
            
            public void run() {
               counter.increment();
            }
         }).start(); 
      }  
      Thread.sleep(6000);
      System.out.println("Final number (should be 1000): " + counter.value());
   }  
}

Isso pode produzir o seguinte resultado, dependendo da velocidade do computador e da intercalação de thread.

Resultado

Final number (should be 1000): 1000

Exemplo

import java.util.concurrent.atomic.AtomicInteger;

public class TestThread {

   static class Counter {
      private AtomicInteger c = new AtomicInteger(0);

      public void increment() {
         c.getAndIncrement();
      }

      public int value() {
         return c.get();
      }
   }
   
   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {

         new Thread(new Runnable() {
            public void run() {
               counter.increment();
            }
         }).start(); 
      }  
      Thread.sleep(6000);
      System.out.println("Final number (should be 1000): " + counter.value());
   }
}

Isso produzirá o seguinte resultado.

Resultado

Final number (should be 1000): 1000

Uma classe java.util.concurrent.atomic.AtomicLong fornece operações no valor longo subjacente que podem ser lidos e gravados atomicamente e também contém operações atômicas avançadas. AtomicLong suporta operações atômicas na variável longa subjacente. Ele possui métodos get e set que funcionam como leituras e gravações em variáveis ​​voláteis. Ou seja, um conjunto tem um relacionamento acontece antes com qualquer obtenção subsequente na mesma variável. O método atômico compareAndSet também possui esses recursos de consistência de memória.

Métodos AtomicLong

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

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

public long addAndGet(long delta)

Adiciona atomicamente o valor fornecido ao valor atual.

2

public boolean compareAndSet(long expect, long update)

Define atomicamente o valor para o valor atualizado fornecido se o valor atual for igual ao valor esperado.

3

public long decrementAndGet()

Diminui atomicamente em um o valor atual.

4

public double doubleValue()

Retorna o valor do número especificado como um duplo.

5

public float floatValue()

Retorna o valor do número especificado como um float.

6

public long get()

Obtém o valor atual.

7

public long getAndAdd(long delta)

Atomicamente adiciona o valor fornecido ao valor atual.

8

public long getAndDecrement()

Diminui atomicamente em um o valor atual.

9

public long getAndIncrement()

Aumenta atomicamente em um o valor atual.

10

public long getAndSet(long newValue)

Define atomicamente o valor fornecido e retorna o valor antigo.

11

public long incrementAndGet()

Aumenta atomicamente em um o valor atual.

12

public int intValue()

Retorna o valor do número especificado como um inteiro.

13

public void lazySet(long newValue)

Eventualmente, define o valor fornecido.

14

public long longValue()

Retorna o valor do número especificado como um longo.

15

public void set(long newValue)

Define o valor fornecido.

16

public String toString()

Retorna a representação de String do valor atual.

17

public boolean weakCompareAndSet(long expect, long update)

Define atomicamente o valor para o valor atualizado fornecido se o valor atual for igual ao valor esperado.

Exemplo

O seguinte programa TestThread mostra uma implementação segura de contador usando AtomicLong em ambiente baseado em thread.

import java.util.concurrent.atomic.AtomicLong;

public class TestThread {

   static class Counter {
      private AtomicLong c = new AtomicLong(0);

      public void increment() {
         c.getAndIncrement();
      }

      public long value() {
         return c.get();
      }
   }

   public static void main(final String[] arguments) throws InterruptedException {
      final Counter counter = new Counter();
      
      //1000 threads
      for(int i = 0; i < 1000 ; i++) {
         
         new Thread(new Runnable() {
            
            public void run() {
               counter.increment();
            }
         }).start();	
      }
      Thread.sleep(6000);			   		  
      System.out.println("Final number (should be 1000): " + counter.value());
   }
}

Isso produzirá o seguinte resultado.

Resultado

Final number (should be 1000): 1000

Uma classe java.util.concurrent.atomic.AtomicBoolean fornece operações no valor booleano subjacente que pode ser lido e escrito atomicamente e também contém operações atômicas avançadas. AtomicBoolean oferece suporte a operações atômicas na variável booleana subjacente. Ele possui métodos get e set que funcionam como leituras e gravações em variáveis ​​voláteis. Ou seja, um conjunto tem um relacionamento acontece antes com qualquer obtenção subsequente na mesma variável. O método atômico compareAndSet também possui esses recursos de consistência de memória.

Métodos AtomicBoolean

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

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

public boolean compareAndSet(boolean expect, boolean update)

Define atomicamente o valor para o valor atualizado fornecido se o valor atual == o valor esperado.

2

public boolean get()

Retorna o valor atual.

3

public boolean getAndSet(boolean newValue)

Define atomicamente o valor fornecido e retorna o valor anterior.

4

public void lazySet(boolean newValue)

Eventualmente, define o valor fornecido.

5

public void set(boolean newValue)

Define incondicionalmente o valor fornecido.

6

public String toString()

Retorna a representação de String do valor atual.

7

public boolean weakCompareAndSet(boolean expect, boolean update)

Define atomicamente o valor para o valor atualizado fornecido se o valor atual == o valor esperado.

Exemplo

O seguinte programa TestThread mostra o uso da variável AtomicBoolean em um ambiente baseado em thread.

import java.util.concurrent.atomic.AtomicBoolean;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final AtomicBoolean atomicBoolean = new AtomicBoolean(false);

      new Thread("Thread 1") {

         public void run() {

            while(true) {
               System.out.println(Thread.currentThread().getName() 
                  +" Waiting for Thread 2 to set Atomic variable to true. Current value is "
                  + atomicBoolean.get());

               if(atomicBoolean.compareAndSet(true, false)) {
                  System.out.println("Done!");
                  break;
               }
            }
         };
      }.start();

      new Thread("Thread 2") {

         public void run() {
            System.out.println(Thread.currentThread().getName() +
               ", Atomic Variable: " +atomicBoolean.get()); 
            System.out.println(Thread.currentThread().getName() +
               " is setting the variable to true ");
            atomicBoolean.set(true);
            System.out.println(Thread.currentThread().getName() +
               ", Atomic Variable: " +atomicBoolean.get()); 
         };
      }.start();
   }
}

Isso produzirá o seguinte resultado.

Resultado

Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2, Atomic Variable: false
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Thread 2 is setting the variable to true
Thread 2, Atomic Variable: true
Thread 1 Waiting for Thread 2 to set Atomic variable to true. Current value is false
Done!

Uma classe java.util.concurrent.atomic.AtomicReference fornece operações na referência de objeto subjacente que podem ser lidas e gravadas atomicamente e também contém operações atômicas avançadas. AtomicReference oferece suporte a operações atômicas na variável de referência de objeto subjacente. Ele possui métodos get e set que funcionam como leituras e gravações em variáveis ​​voláteis. Ou seja, um conjunto tem um relacionamento acontece antes com qualquer obtenção subsequente na mesma variável. O método atômico compareAndSet também possui esses recursos de consistência de memória.

Métodos AtomicReference

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

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

public boolean compareAndSet(V expect, V update)

Define atomicamente o valor para o valor atualizado fornecido se o valor atual == o valor esperado.

2

public boolean get()

Retorna o valor atual.

3

public boolean getAndSet(V newValue)

Define atomicamente o valor fornecido e retorna o valor anterior.

4

public void lazySet(V newValue)

Eventualmente, define o valor fornecido.

5

public void set(V newValue)

Define incondicionalmente o valor fornecido.

6

public String toString()

Retorna a representação de String do valor atual.

7

public boolean weakCompareAndSet(V expect, V update)

Define atomicamente o valor para o valor atualizado fornecido se o valor atual == o valor esperado.

Exemplo

O seguinte programa TestThread mostra o uso da variável AtomicReference em ambiente baseado em encadeamento.

import java.util.concurrent.atomic.AtomicReference;

public class TestThread {
   private static String message = "hello";
   private static AtomicReference<String> atomicReference;

   public static void main(final String[] arguments) throws InterruptedException {
      atomicReference = new AtomicReference<String>(message);
      
      new Thread("Thread 1") {
         
         public void run() {
            atomicReference.compareAndSet(message, "Thread 1");
            message = message.concat("-Thread 1!");
         };
      }.start();

      System.out.println("Message is: " + message);
      System.out.println("Atomic Reference of Message is: " + atomicReference.get());
   }
}

Isso produzirá o seguinte resultado.

Resultado

Message is: hello
Atomic Reference of Message is: Thread 1

Uma classe java.util.concurrent.atomic.AtomicIntegerArray fornece operações na matriz int subjacente que podem ser lidas e gravadas atomicamente e também contém operações atômicas avançadas. AtomicIntegerArray oferece suporte a operações atômicas na variável subjacente da matriz int. Ele possui métodos get e set que funcionam como leituras e gravações em variáveis ​​voláteis. Ou seja, um conjunto tem um relacionamento acontece antes com qualquer obtenção subsequente na mesma variável. O método atômico compareAndSet também possui esses recursos de consistência de memória.

Métodos AtomicIntegerArray

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

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

public int addAndGet(int i, int delta)

Adiciona atomicamente o valor fornecido ao elemento no índice i.

2

public boolean compareAndSet(int i, int expect, int update)

Define atomicamente o elemento na posição i para o valor atualizado fornecido se o valor atual == o valor esperado.

3

public int decrementAndGet(int i)

Diminui atomicamente em um o elemento no índice i.

4

public int get(int i)

Obtém o valor atual na posição i.

5

public int getAndAdd(int i, int delta)

Adiciona atomicamente o valor fornecido ao elemento no índice i.

6

public int getAndDecrement(int i)

Diminui atomicamente em um o elemento no índice i.

7

public int getAndIncrement(int i)

Incrementa atomicamente em um o elemento no índice i.

8

public int getAndSet(int i, int newValue)

Define atomicamente o elemento na posição i com o valor fornecido e retorna o valor antigo.

9

public int incrementAndGet(int i)

Incrementa atomicamente em um o elemento no índice i.

10

public void lazySet(int i, int newValue)

Eventualmente, define o elemento na posição i com o valor fornecido.

11

public int length()

Retorna o comprimento da matriz.

12

public void set(int i, int newValue)

Define o elemento na posição i para o valor fornecido.

13

public String toString()

Retorna a representação de String dos valores atuais da matriz.

14

public boolean weakCompareAndSet(int i, int expect, int update)

Define atomicamente o elemento na posição i para o valor atualizado fornecido se o valor atual == o valor esperado.

Exemplo

O seguinte programa TestThread mostra o uso da variável AtomicIntegerArray em ambiente baseado em thread.

import java.util.concurrent.atomic.AtomicIntegerArray;

public class TestThread {
   private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);

   public static void main(final String[] arguments) throws InterruptedException {
      
      for (int i = 0; i<atomicIntegerArray.length(); i++) {
         atomicIntegerArray.set(i, 1);
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();

      System.out.println("Values: ");

      for (int i = 0; i<atomicIntegerArray.length(); i++) {
         System.out.print(atomicIntegerArray.get(i) + " ");
      }
   }

   static class Increment implements Runnable {

      public void run() {

         for(int i = 0; i<atomicIntegerArray.length(); i++) {
            int add = atomicIntegerArray.incrementAndGet(i);
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {

      public void run() {

         for(int i = 0; i<atomicIntegerArray.length(); i++) {
            boolean swapped = atomicIntegerArray.compareAndSet(i, 2, 3);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId()
                  + ", index " +i + ", value: 3");
            }
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Thread 10, index 0, value: 2
Thread 10, index 1, value: 2
Thread 10, index 2, value: 2
Thread 11, index 0, value: 3
Thread 10, index 3, value: 2
Thread 11, index 1, value: 3
Thread 11, index 2, value: 3
Thread 10, index 4, value: 2
Thread 11, index 3, value: 3
Thread 10, index 5, value: 2
Thread 10, index 6, value: 2
Thread 11, index 4, value: 3
Thread 10, index 7, value: 2
Thread 11, index 5, value: 3
Thread 10, index 8, value: 2
Thread 11, index 6, value: 3
Thread 10, index 9, value: 2
Thread 11, index 7, value: 3
Thread 11, index 8, value: 3
Thread 11, index 9, value: 3
Values:
3 3 3 3 3 3 3 3 3 3

Uma classe java.util.concurrent.atomic.AtomicLongArray fornece operações em longa matriz subjacente que podem ser lidas e gravadas atomicamente e também contém operações atômicas avançadas. AtomicLongArray oferece suporte a operações atômicas na variável de matriz longa subjacente. Ele possui métodos get e set que funcionam como leituras e gravações em variáveis ​​voláteis. Ou seja, um conjunto tem um relacionamento acontece antes com qualquer obtenção subsequente na mesma variável. O método atômico compareAndSet também possui esses recursos de consistência de memória.

Métodos AtomicLongArray

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

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

public long addAndGet(int i, long delta)

Adiciona atomicamente o valor fornecido ao elemento no índice i.

2

public boolean compareAndSet(int i, long expect, long update)

Define atomicamente o elemento na posição i para o valor atualizado fornecido se o valor atual == o valor esperado.

3

public long decrementAndGet(int i)

Diminui atomicamente em um o elemento no índice i.

4

public long get(int i)

Obtém o valor atual na posição i.

5

public long getAndAdd(int i, long delta)

Adiciona atomicamente o valor fornecido ao elemento no índice i.

6

public long getAndDecrement(int i)

Diminui atomicamente em um o elemento no índice i.

7

public long getAndIncrement(int i)

Incrementa atomicamente em um o elemento no índice i.

8

public long getAndSet(int i, long newValue)

Define atomicamente o elemento na posição i com o valor fornecido e retorna o valor antigo.

9

public long incrementAndGet(int i)

Incrementa atomicamente em um o elemento no índice i.

10

public void lazySet(int i, long newValue)

Eventualmente, define o elemento na posição i com o valor fornecido.

11

public int length()

Retorna o comprimento da matriz.

12

public void set(int i, long newValue)

Define o elemento na posição i para o valor fornecido.

13

public String toString()

Retorna a representação de String dos valores atuais da matriz.

14

public boolean weakCompareAndSet(int i, long expect, long update)

Define atomicamente o elemento na posição i para o valor atualizado fornecido se o valor atual == o valor esperado.

Exemplo

O seguinte programa TestThread mostra o uso da variável AtomicIntegerArray em ambiente baseado em thread.

import java.util.concurrent.atomic.AtomicLongArray;

public class TestThread {
   private static AtomicLongArray atomicLongArray = new AtomicLongArray(10);

   public static void main(final String[] arguments) throws InterruptedException {

      for (int i = 0; i<atomicLongArray.length(); i++) {
         atomicLongArray.set(i, 1);
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();

      System.out.println("Values: ");
      
      for (int i = 0; i<atomicLongArray.length(); i++) {
         System.out.print(atomicLongArray.get(i) + " ");
      }
   }  

   static class Increment implements Runnable {

      public void run() {

         for(int i = 0; i<atomicLongArray.length(); i++) {
            long add = atomicLongArray.incrementAndGet(i);
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {

      public void run() {

         for(int i = 0; i<atomicLongArray.length(); i++) {
            boolean swapped = atomicLongArray.compareAndSet(i, 2, 3);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId()
                  + ", index " +i + ", value: 3");
            }
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Thread 9, index 0, value: 2
Thread 10, index 0, value: 3
Thread 9, index 1, value: 2
Thread 9, index 2, value: 2
Thread 9, index 3, value: 2
Thread 9, index 4, value: 2
Thread 10, index 1, value: 3
Thread 9, index 5, value: 2
Thread 10, index 2, value: 3
Thread 9, index 6, value: 2
Thread 10, index 3, value: 3
Thread 9, index 7, value: 2
Thread 10, index 4, value: 3
Thread 9, index 8, value: 2
Thread 9, index 9, value: 2
Thread 10, index 5, value: 3
Thread 10, index 6, value: 3
Thread 10, index 7, value: 3
Thread 10, index 8, value: 3
Thread 10, index 9, value: 3
Values: 
3 3 3 3 3 3 3 3 3 3

Uma classe java.util.concurrent.atomic.AtomicReferenceArray fornece operações na matriz de referência subjacente que podem ser lidas e gravadas atomicamente e também contém operações atômicas avançadas. AtomicReferenceArray oferece suporte a operações atômicas na variável de matriz de referência subjacente. Ele possui métodos get e set que funcionam como leituras e gravações em variáveis ​​voláteis. Ou seja, um conjunto tem um relacionamento acontece antes com qualquer obtenção subsequente na mesma variável. O método atômico compareAndSet também possui esses recursos de consistência de memória.

Métodos AtomicReferenceArray

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

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

public boolean compareAndSet(int i, E expect, E update)

Define atomicamente o elemento na posição i para o valor atualizado fornecido se o valor atual == o valor esperado.

2

public E get(int i)

Obtém o valor atual na posição i.

3

public E getAndSet(int i, E newValue)

Define atomicamente o elemento na posição i com o valor fornecido e retorna o valor antigo.

4

public void lazySet(int i, E newValue)

Eventualmente, define o elemento na posição i com o valor fornecido.

5

public int length()

Retorna o comprimento da matriz.

6

public void set(int i, E newValue)

Define o elemento na posição i para o valor fornecido.

7

public String toString()

Retorna a representação de String dos valores atuais da matriz.

8

public boolean weakCompareAndSet(int i, E expect, E update)

Define atomicamente o elemento na posição i para o valor atualizado fornecido se o valor atual == o valor esperado.

Exemplo

O seguinte programa TestThread mostra o uso da variável AtomicReferenceArray em ambiente baseado em thread.

import java.util.concurrent.atomic.AtomicReferenceArray;

public class TestThread {
   private static String[] source = new String[10];
   private static AtomicReferenceArray<String> atomicReferenceArray 
      = new AtomicReferenceArray<String>(source);

   public static void main(final String[] arguments) throws InterruptedException {

      for (int i = 0; i<atomicReferenceArray.length(); i++) {
         atomicReferenceArray.set(i, "item-2");
      }

      Thread t1 = new Thread(new Increment());
      Thread t2 = new Thread(new Compare());
      t1.start();
      t2.start();

      t1.join();
      t2.join();		
   }  

   static class Increment implements Runnable {
      
      public void run() {
         
         for(int i = 0; i<atomicReferenceArray.length(); i++) {
            String add = atomicReferenceArray.getAndSet(i,"item-"+ (i+1));
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ add);
         }
      }
   }

   static class Compare implements Runnable {
      
      public void run() {
         
         for(int i = 0; i<atomicReferenceArray.length(); i++) {
            System.out.println("Thread " + Thread.currentThread().getId() 
               + ", index " +i + ", value: "+ atomicReferenceArray.get(i));
            boolean swapped = atomicReferenceArray.compareAndSet(i, "item-2", "updated-item-2");
            System.out.println("Item swapped: " + swapped);
            
            if(swapped) {
               System.out.println("Thread " + Thread.currentThread().getId() 
                  + ", index " +i + ", updated-item-2");
            }
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Thread 9, index 0, value: item-2
Thread 10, index 0, value: item-1
Item swapped: false
Thread 10, index 1, value: item-2
Item swapped: true
Thread 9, index 1, value: updated-item-2
Thread 10, index 1, updated-item-2
Thread 10, index 2, value: item-3
Item swapped: false
Thread 10, index 3, value: item-2
Item swapped: true
Thread 10, index 3, updated-item-2
Thread 10, index 4, value: item-2
Item swapped: true
Thread 10, index 4, updated-item-2
Thread 10, index 5, value: item-2
Item swapped: true
Thread 10, index 5, updated-item-2
Thread 10, index 6, value: item-2
Thread 9, index 2, value: item-2
Item swapped: true
Thread 9, index 3, value: updated-item-2
Thread 10, index 6, updated-item-2
Thread 10, index 7, value: item-2
Thread 9, index 4, value: updated-item-2
Item swapped: true
Thread 9, index 5, value: updated-item-2
Thread 10, index 7, updated-item-2
Thread 9, index 6, value: updated-item-2
Thread 10, index 8, value: item-2
Thread 9, index 7, value: updated-item-2
Item swapped: true
Thread 9, index 8, value: updated-item-2
Thread 10, index 8, updated-item-2
Thread 9, index 9, value: item-2
Thread 10, index 9, value: item-10
Item swapped: false

Uma interface java.util.concurrent.Executor é uma interface simples para suportar o lançamento de novas tarefas.

Métodos ExecutorService

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

void execute(Runnable command)

Executa o comando fornecido em algum momento no futuro.

Exemplo

O seguinte programa TestThread mostra o uso da interface Executor em ambiente baseado em thread.

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      Executor executor = Executors.newCachedThreadPool();
      executor.execute(new Task());
      ThreadPoolExecutor pool = (ThreadPoolExecutor)executor;
      pool.shutdown();
   }  

   static class Task implements Runnable {
      
      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed");
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Running Task!
Task Completed

Uma interface java.util.concurrent.ExecutorService é uma subinterface da interface do Executor e adiciona recursos para gerenciar o ciclo de vida, tanto das tarefas individuais quanto do próprio executor.

Métodos ExecutorService

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

boolean awaitTermination(long timeout, TimeUnit unit)

Bloqueia até que todas as tarefas tenham concluído a execução após uma solicitação de desligamento, ou o tempo limite ocorra ou o thread atual seja interrompido, o que ocorrer primeiro.

2

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)

Executa as tarefas fornecidas, retornando uma lista de Futuros com seus status e resultados quando todos concluídos.

3

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

Executa as tarefas fornecidas, retornando uma lista de Futuros com seus status e resultados quando todos são concluídos ou o tempo limite expira, o que ocorrer primeiro.

4

<T> T invokeAny(Collection<? extends Callable<T>> tasks)

Executa as tarefas fornecidas, retornando o resultado de uma que foi concluída com êxito (ou seja, sem lançar uma exceção), se houver.

5

<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

Executa as tarefas fornecidas, retornando o resultado de uma que foi concluída com êxito (ou seja, sem lançar uma exceção), se algum o fizer antes que o tempo limite determinado expire.
6

boolean isShutdown()

Retorna verdadeiro se este executor foi desligado.

7

boolean isTerminated()

Retorna verdadeiro se todas as tarefas foram concluídas após o desligamento.

8

void shutdown()

Inicia um desligamento ordenado no qual as tarefas enviadas anteriormente são executadas, mas nenhuma tarefa nova será aceita.

9

List<Runnable> shutdownNow()

Tenta interromper todas as tarefas em execução ativa, interrompe o processamento de tarefas em espera e retorna uma lista das tarefas que estavam aguardando execução.

10

<T> Future<T> submit(Callable<T> task)

Envia uma tarefa de retorno de valor para execução e retorna um Future representando os resultados pendentes da tarefa.

11

Future<?> submit(Runnable task)

Envia uma tarefa Executável para execução e retorna um Future que representa essa tarefa.

12

<T> Future<T> submit(Runnable task, T result)

Envia uma tarefa Executável para execução e retorna um Future que representa essa tarefa.

Exemplo

O seguinte programa TestThread mostra o uso da interface ExecutorService em um ambiente baseado em thread.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newSingleThreadExecutor();

      try {
         executor.submit(new Task());
         System.out.println("Shutdown executor");
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         System.err.println("tasks interrupted");
      } finally {

         if (!executor.isTerminated()) {
            System.err.println("cancel non-finished tasks");
         }
         executor.shutdownNow();
         System.out.println("shutdown finished");
      }
   }

   static class Task implements Runnable {
      
      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 20);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }	   
}

Isso produzirá o seguinte resultado.

Resultado

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:302)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
	at TestThread$Task.run(TestThread.java:39)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

Uma interface java.util.concurrent.ScheduledExecutorService é uma subinterface da interface ExecutorService e oferece suporte à execução futura e / ou periódica de tarefas.

Métodos ScheduledExecutorService

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

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

Cria e executa um ScheduledFuture que é ativado após um determinado atraso.

2

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

Cria e executa uma ação instantânea que é ativada após um determinado atraso.

3

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

Cria e executa uma ação periódica que é habilitada primeiro após o atraso inicial fornecido e, subsequentemente, com o período determinado; isto é, as execuções começarão após initialDelay, initialDelay + período, initialDelay + 2 * período e assim por diante.

4

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

Cria e executa uma ação periódica que se torna habilitada primeiro após o atraso inicial dado e, subsequentemente, com o atraso dado entre o término de uma execução e o início da próxima.

Exemplo

O seguinte programa TestThread mostra o uso da interface ScheduledExecutorService em um ambiente baseado em thread.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }

   static class BeepTask implements Runnable {
      
      public void run() {
         System.out.println("beep");      
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

beep
beep
beep
beep

Um pool de thread fixo pode ser obtido chamando o método estático newFixedThreadPool () da classe Executors.

Sintaxe

ExecutorService fixedPool = Executors.newFixedThreadPool(2);

Onde

  • No máximo 2 threads estarão ativos para processar tarefas.

  • Se mais de 2 threads forem enviados, eles serão mantidos em uma fila até que os threads se tornem disponíveis.

  • Um novo encadeamento é criado para assumir seu lugar se um encadeamento for encerrado devido a uma falha durante o encerramento da execução no executor ainda não foi chamado.

  • Qualquer thread existe até que o pool seja encerrado.

Exemplo

O programa TestThread a seguir mostra o uso do método newFixedThreadPool em ambiente baseado em thread.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newFixedThreadPool(2);

      // Cast the object to its class type
      ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + pool.getCorePoolSize());
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
               TimeUnit.SECONDS.sleep(duration);
            
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Largest executions: 0
Maximum allowed threads: 2
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 2
Largest executions: 2
Maximum allowed threads: 2
Current threads in pool: 2
Currently executing threads: 1
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1

Um pool de threads em cache pode ser obtido chamando o método estático newCachedThreadPool () da classe Executors.

Sintaxe

ExecutorService executor = Executors.newCachedThreadPool();

Onde

  • O método newCachedThreadPool cria um executor com um pool de thread expansível.

  • Esse executor é adequado para aplicativos que iniciam muitas tarefas de curta duração.

Exemplo

O seguinte programa TestThread mostra o uso do método newCachedThreadPool em ambiente baseado em thread.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newCachedThreadPool();

      // Cast the object to its class type
      ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + pool.getCorePoolSize());
      System.out.println("Largest executions: "
         + pool.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + pool.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + pool.getPoolSize());
      System.out.println("Currently executing threads: "
         + pool.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + pool.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {
         
         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
               TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-1
Running Task! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-2
Task Completed! Thread Name: pool-1-thread-1

Um pool de thread agendado pode ser obtido chamando o método estático newScheduledThreadPool () da classe Executors.

Sintaxe

ExecutorService executor = Executors.newScheduledThreadPool(1);

Exemplo

O programa TestThread a seguir mostra o uso do método newScheduledThreadPool em ambiente baseado em thread.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }  

   static class BeepTask implements Runnable {

      public void run() {
         System.out.println("beep");      
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

beep
beep
beep
beep

Um único pool de threads pode ser obtido chamando o método estático newSingleThreadExecutor () da classe Executors.

Sintaxe

ExecutorService executor = Executors.newSingleThreadExecutor();

Onde o método newSingleThreadExecutor cria um executor que executa uma única tarefa por vez.

Exemplo

O seguinte programa TestThread mostra o uso do método newSingleThreadExecutor em ambiente baseado em thread.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      ExecutorService executor = Executors.newSingleThreadExecutor();

      try {
         executor.submit(new Task());
         System.out.println("Shutdown executor");
         executor.shutdown();
         executor.awaitTermination(5, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         System.err.println("tasks interrupted");
      } finally {

         if (!executor.isTerminated()) {
            System.err.println("cancel non-finished tasks");
         }
         executor.shutdownNow();
         System.out.println("shutdown finished");
      }
   }

   static class Task implements Runnable {
      
      public void run() {

         try {
            Long duration = (long) (Math.random() * 20);
            System.out.println("Running Task!");
            TimeUnit.SECONDS.sleep(duration);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:302)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:328)
	at TestThread$Task.run(TestThread.java:39) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

java.util.concurrent.ThreadPoolExecutor é um ExecutorService para executar cada tarefa enviada usando um dos vários threads em pool, normalmente configurados usando os métodos de fábrica do Executors. Ele também fornece vários métodos utilitários para verificar as estatísticas dos threads atuais e controlá-los.

Métodos ThreadPoolExecutor

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

protected void afterExecute(Runnable r, Throwable t)

Método invocado após a conclusão da execução do Runnable fornecido.

2

void allowCoreThreadTimeOut(boolean value)

Define a política que rege se os threads principais podem expirar e terminar se nenhuma tarefa chegar dentro do tempo de manutenção de atividade, sendo substituídos se necessário quando novas tarefas chegarem.

3

boolean allowsCoreThreadTimeOut()

Retorna verdadeiro se este pool permitir que os threads principais expirem e terminem se nenhuma tarefa chegar dentro do tempo keepAlive, sendo substituídos se necessário quando novas tarefas chegarem.

4

boolean awaitTermination(long timeout, TimeUnit unit)

Bloqueia até que todas as tarefas tenham concluído a execução após uma solicitação de desligamento, ou o tempo limite ocorra ou o thread atual seja interrompido, o que ocorrer primeiro.

5

protected void beforeExecute(Thread t, Runnable r)

Método invocado antes de executar o Runnable fornecido no thread fornecido.

6

void execute(Runnable command)

Executa a tarefa fornecida em algum momento no futuro.

7

protected void finalize()

Invoca o desligamento quando este executor não é mais referenciado e não possui threads.

8

int getActiveCount()

Retorna o número aproximado de threads que estão executando tarefas ativamente.

9

long getCompletedTaskCount()

Retorna o número total aproximado de tarefas que concluíram a execução.

10

int getCorePoolSize()

Retorna o número principal de threads.

11

long getKeepAliveTime(TimeUnit unit)

Retorna o tempo de manutenção da atividade do encadeamento, que é a quantidade de tempo que os encadeamentos em excesso do tamanho do pool principal podem permanecer ociosos antes de serem encerrados.

12

int getLargestPoolSize()

Retorna o maior número de threads que já estiveram simultaneamente no pool.

13

int getMaximumPoolSize()

Retorna o número máximo permitido de threads.

14

int getPoolSize()

Retorna o número atual de threads no pool.

15

BlockingQueue getQueue()

Retorna a fila de tarefas usada por este executor.

15

RejectedExecutionHandler getRejectedExecutionHandler()

Retorna o manipulador atual para tarefas não executáveis.

16

long getTaskCount()

Retorna o número total aproximado de tarefas que já foram agendadas para execução.

17

ThreadFactory getThreadFactory()

Retorna a fábrica de threads usada para criar novas threads.

18

boolean isShutdown()

Retorna verdadeiro se este executor foi desligado.

19

boolean isTerminated()

Retorna verdadeiro se todas as tarefas foram concluídas após o desligamento.

20

boolean isTerminating()

Retorna verdadeiro se este executor está em processo de encerramento após shutdown () ou shutdownNow (), mas não foi encerrado completamente.

21

int prestartAllCoreThreads()

Inicia todos os threads principais, fazendo com que esperem ociosamente pelo trabalho.

22

boolean prestartCoreThread()

Inicia um thread principal, fazendo com que ele espere ociosamente pelo trabalho.

23

void purge()

Tenta remover da fila de trabalho todas as tarefas futuras que foram canceladas.

24

boolean remove(Runnable task)

Remove esta tarefa da fila interna do executor, se estiver presente, fazendo com que não seja executada se ainda não tiver sido iniciada.

25

void setCorePoolSize(int corePoolSize)

Define o número principal de threads.

26

void setKeepAliveTime(long time, TimeUnit unit)

Define o limite de tempo durante o qual os threads podem permanecer inativos antes de serem encerrados.

27

void setMaximumPoolSize(int maximumPoolSize)

Define o número máximo permitido de threads.

28

void setRejectedExecutionHandler(RejectedExecutionHandler handler)

Define um novo manipulador para tarefas não executáveis.

29

void setThreadFactory(ThreadFactory threadFactory)

Define a fábrica de linhas usada para criar novas linhas.

30

void shutdown()

Inicia um desligamento ordenado no qual as tarefas enviadas anteriormente são executadas, mas nenhuma tarefa nova será aceita.

31

List<Runnable> shutdownNow()

Tenta interromper todas as tarefas em execução ativa, interrompe o processamento de tarefas em espera e retorna uma lista das tarefas que estavam aguardando execução.

32

protected void terminated()

Método invocado quando o Executor é encerrado.

33

String toString()

Retorna uma string que identifica este pool, bem como seu estado, incluindo indicações de estado de execução e contagens estimadas de trabalhadores e tarefas.

Exemplo

O seguinte programa TestThread mostra o uso da interface ThreadPoolExecutor em um ambiente baseado em thread.

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThread {
	
   public static void main(final String[] arguments) throws InterruptedException {
      ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newCachedThreadPool();

      //Stats before tasks execution
      System.out.println("Largest executions: "
         + executor.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + executor.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + executor.getPoolSize());
      System.out.println("Currently executing threads: "
         + executor.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + executor.getTaskCount());

      executor.submit(new Task());
      executor.submit(new Task());

      //Stats after tasks execution
      System.out.println("Core threads: " + executor.getCorePoolSize());
      System.out.println("Largest executions: "
         + executor.getLargestPoolSize());
      System.out.println("Maximum allowed threads: "
         + executor.getMaximumPoolSize());
      System.out.println("Current threads in pool: "
         + executor.getPoolSize());
      System.out.println("Currently executing threads: "
         + executor.getActiveCount());
      System.out.println("Total number of threads(ever scheduled): "
         + executor.getTaskCount());

      executor.shutdown();
   }  

   static class Task implements Runnable {

      public void run() {

         try {
            Long duration = (long) (Math.random() * 5);
            System.out.println("Running Task! Thread Name: " +
               Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(duration);
            System.out.println("Task Completed! Thread Name: " +
               Thread.currentThread().getName());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Largest executions: 0
Maximum allowed threads: 2147483647
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 0
Largest executions: 2
Maximum allowed threads: 2147483647
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-2
Running Task! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-2

java.util.concurrent.ScheduledThreadPoolExecutor é uma subclasse de ThreadPoolExecutor e pode agendar comandos adicionais para execução após um determinado atraso ou para execução periódica.

Métodos ScheduledThreadPoolExecutor

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

protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task)

Modifica ou substitui a tarefa usada para executar um chamável.

2

protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task)

Modifica ou substitui a tarefa usada para executar um executável.

3

void execute(Runnable command)

Executa o comando com zero de atraso necessário.

4

boolean getContinueExistingPeriodicTasksAfterShutdownPolicy()

Obtém a política de continuar executando as tarefas periódicas existentes, mesmo quando o executor foi desligado.

5

boolean getExecuteExistingDelayedTasksAfterShutdownPolicy()

Obtém a política sobre a execução de tarefas atrasadas existentes, mesmo quando este executor foi desligado.

6

BlockingQueue<Runnable> getQueue()

Retorna a fila de tarefas usada por este executor.

7

boolean getRemoveOnCancelPolicy()

Obtém a política sobre se as tarefas canceladas devem ser removidas imediatamente da fila de trabalho no momento do cancelamento.

8

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

Cria e executa um ScheduledFuture que é ativado após um determinado atraso.

9

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

Cria e executa uma ação instantânea que é ativada após um determinado atraso.

10

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

Cria e executa uma ação periódica que é habilitada primeiro após o atraso inicial fornecido e, subsequentemente, com o período determinado; isto é, as execuções começarão após initialDelay, initialDelay + período, initialDelay + 2 * período e assim por diante.

11

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

Cria e executa uma ação periódica que se torna habilitada primeiro após o atraso inicial dado e, subsequentemente, com o atraso dado entre o término de uma execução e o início da próxima.

12

void setContinueExistingPeriodicTasksAfterShutdownPolicy (boolean value)

Define a política de continuar ou não a execução de tarefas periódicas existentes, mesmo quando este executor foi desligado.

13

void setExecuteExistingDelayedTasksAfterShutdownPolicy (boolean value)

Define a política de execução de tarefas atrasadas existentes, mesmo quando este executor foi desligado.

14

void setRemoveOnCancelPolicy(boolean value)

Define a política se as tarefas canceladas devem ser removidas imediatamente da fila de trabalho no momento do cancelamento.

15

void shutdown()

Inicia um desligamento ordenado no qual as tarefas enviadas anteriormente são executadas, mas nenhuma tarefa nova será aceita.

16

List<Runnable> shutdownNow()

Tenta interromper todas as tarefas em execução ativa, interrompe o processamento de tarefas em espera e retorna uma lista das tarefas que estavam aguardando execução.

17

<T> Future<T> submit(Callable<T> task)

Envia uma tarefa de retorno de valor para execução e retorna um Future representando os resultados pendentes da tarefa.

18

Future<?> submit(Runnable task)

Envia uma tarefa Executável para execução e retorna um Future que representa essa tarefa.

19

<T> Future<T> submit(Runnable task, T result)

Envia uma tarefa Executável para execução e retorna um Future que representa essa tarefa.

Exemplo

O programa TestThread a seguir mostra o uso da interface ScheduledThreadPoolExecutor em um ambiente baseado em thread.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      final ScheduledThreadPoolExecutor scheduler = 
         (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(1);

      final ScheduledFuture<?> beepHandler = 
         scheduler.scheduleAtFixedRate(new BeepTask(), 2, 2, TimeUnit.SECONDS);

      scheduler.schedule(new Runnable() {

         @Override
         public void run() {
            beepHandler.cancel(true);
            scheduler.shutdown();			
         }
      }, 10, TimeUnit.SECONDS);
   }  

   static class BeepTask implements Runnable {
      
      public void run() {
         System.out.println("beep");      
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

beep
beep
beep
beep

O objeto java.util.concurrent.Callable pode retornar o resultado calculado feito por um encadeamento em contraste com a interface executável que só pode executar o encadeamento. O objeto Callable retorna o objeto Future que fornece métodos para monitorar o progresso de uma tarefa sendo executada por um thread. O objeto Future pode ser usado para verificar o status de um Callable e, em seguida, recuperar o resultado do Callable assim que o thread estiver concluído. Ele também fornece funcionalidade de tempo limite.

Sintaxe

//submit the callable using ThreadExecutor
//and get the result as a Future object
Future<Long> result10 = executor.submit(new FactorialService(10));
 
//get the result using get method of the Future object
//get method waits till the thread execution and then return the result of the execution.
Long factorial10 = result10.get();

Exemplo

O seguinte programa TestThread mostra o uso de Futures e Callables em ambiente baseado em thread.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException,
      ExecutionException {

      ExecutorService executor = Executors.newSingleThreadExecutor();

      System.out.println("Factorial Service called for 10!");
      Future<Long> result10 = executor.submit(new FactorialService(10));

      System.out.println("Factorial Service called for 20!");
      Future<Long> result20 = executor.submit(new FactorialService(20));

      Long factorial10 = result10.get();
      System.out.println("10! = " + factorial10);

      Long factorial20 = result20.get();
      System.out.println("20! = " + factorial20);

      executor.shutdown();
   }  

   static class FactorialService implements Callable<Long> {
      private int number;

      public FactorialService(int number) {
         this.number = number;
      }

      @Override
      public Long call() throws Exception {
         return factorial();
      }

      private Long factorial() throws InterruptedException {
         long result = 1; 
         
         while (number != 0) { 
            result = number * result; 
            number--; 
            Thread.sleep(100); 
         }
         return result;	
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Factorial Service called for 10!
Factorial Service called for 20!
10! = 3628800
20! = 2432902008176640000

O framework fork-join permite quebrar uma determinada tarefa em vários trabalhadores e então esperar o resultado para combiná-los. Ele alavanca a capacidade da máquina com vários processadores em grande medida. A seguir estão os principais conceitos e objetos usados ​​na estrutura fork-join.

Garfo

Fork é um processo no qual uma tarefa se divide em subtarefas menores e independentes que podem ser executadas simultaneamente.

Sintaxe

Sum left  = new Sum(array, low, mid);
left.fork();

Aqui, Sum é uma subclasse de RecursiveTask e left.fork () divide a tarefa em subtarefas.

Junte-se

Unir é um processo no qual uma tarefa une todos os resultados das subtarefas assim que a execução das subtarefas é concluída, caso contrário, ela continua esperando.

Sintaxe

left.join();

Aqui à esquerda está um objeto da classe Sum.

ForkJoinPool

é um pool de threads especial projetado para trabalhar com divisão de tarefas fork-and-join.

Sintaxe

ForkJoinPool forkJoinPool = new ForkJoinPool(4);

Aqui está um novo ForkJoinPool com um nível de paralelismo de 4 CPUs.

RecursiveAction

RecursiveAction representa uma tarefa que não retorna nenhum valor.

Sintaxe

class Writer extends RecursiveAction {
   @Override
   protected void compute() { }
}

RecursiveTask

RecursiveTask representa uma tarefa que retorna um valor.

Sintaxe

class Sum extends RecursiveTask<Long> {
   @Override
   protected Long compute() { return null; }
}

Exemplo

O programa TestThread a seguir mostra o uso da estrutura Fork-Join em um ambiente baseado em thread.

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException, 
      ExecutionException {
      
      int nThreads = Runtime.getRuntime().availableProcessors();
      System.out.println(nThreads);
      
      int[] numbers = new int[1000]; 

      for(int i = 0; i < numbers.length; i++) {
         numbers[i] = i;
      }

      ForkJoinPool forkJoinPool = new ForkJoinPool(nThreads);
      Long result = forkJoinPool.invoke(new Sum(numbers,0,numbers.length));
      System.out.println(result);
   }  

   static class Sum extends RecursiveTask<Long> {
      int low;
      int high;
      int[] array;

      Sum(int[] array, int low, int high) {
         this.array = array;
         this.low   = low;
         this.high  = high;
      }

      protected Long compute() {
         
         if(high - low <= 10) {
            long sum = 0;
            
            for(int i = low; i < high; ++i) 
               sum += array[i];
               return sum;
         } else {	    	
            int mid = low + (high - low) / 2;
            Sum left  = new Sum(array, low, mid);
            Sum right = new Sum(array, mid, high);
            left.fork();
            long rightResult = right.compute();
            long leftResult  = left.join();
            return leftResult + rightResult;
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

32
499500

Uma interface java.util.concurrent.BlockingQueue é uma subinterface da interface Queue e, adicionalmente, suporta operações como esperar que a fila se torne vazia antes de recuperar um elemento e esperar que o espaço fique disponível na fila antes de armazenar um elemento .

Métodos BlockingQueue

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

boolean add(E e)

Insere o elemento especificado nesta fila se for possível fazê-lo imediatamente sem violar as restrições de capacidade, retornando true em caso de sucesso e lançando uma IllegalStateException se nenhum espaço estiver disponível no momento.

2

boolean contains(Object o)

Retorna verdadeiro se esta fila contém o elemento especificado.

3

int drainTo(Collection<? super E> c)

Remove todos os elementos disponíveis desta fila e os adiciona à coleção fornecida.

4

int drainTo(Collection<? super E> c, int maxElements)

Remove no máximo o número determinado de elementos disponíveis desta fila e os adiciona à coleção fornecida.

5

boolean offer(E e)

Insere o elemento especificado nesta fila se for possível fazê-lo imediatamente sem violar as restrições de capacidade, retornando verdadeiro em caso de sucesso e falso se nenhum espaço estiver disponível.

6

boolean offer(E e, long timeout, TimeUnit unit)

Insere o elemento especificado nesta fila, aguardando o tempo de espera especificado, se necessário, para que o espaço fique disponível.

7

E poll(long timeout, TimeUnit unit)

Recupera e remove o topo desta fila, aguardando até o tempo de espera especificado, se necessário, para que um elemento fique disponível.

8

void put(E e)

Insere o elemento especificado nesta fila, aguardando, se necessário, que o espaço se torne disponível.

9

int remainingCapacity()

Retorna o número de elementos adicionais que esta fila pode idealmente (na ausência de restrições de memória ou recursos) aceitar sem bloqueio, ou Integer.MAX_VALUE se não houver limite intrínseco.

10

boolean remove(Object o)

Remove uma única instância do elemento especificado desta fila, se estiver presente.

11

E take()

Recupera e remove o topo desta fila, aguardando se necessário até que um elemento esteja disponível.

Exemplo

O programa TestThread a seguir mostra o uso da interface BlockingQueue em um ambiente baseado em thread.

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException {
      BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);

      Producer producer = new Producer(queue);
      Consumer consumer = new Consumer(queue);

      new Thread(producer).start();
      new Thread(consumer).start();

      Thread.sleep(4000);
   }  


   static class Producer implements Runnable {
      private BlockingQueue<Integer> queue;

      public Producer(BlockingQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         Random random = new Random();

         try {
            int result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
            
            result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
            
            result = random.nextInt(100);
            Thread.sleep(1000);
            queue.put(result);
            System.out.println("Added: " + result);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }	   
   }

   static class Consumer implements Runnable {
      private BlockingQueue<Integer> queue;

      public Consumer(BlockingQueue queue) {
         this.queue = queue;
      }
      
      @Override
      public void run() {
         
         try {
            System.out.println("Removed: " + queue.take());
            System.out.println("Removed: " + queue.take());
            System.out.println("Removed: " + queue.take());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}

Isso produzirá o seguinte resultado.

Resultado

Added: 52
Removed: 52
Added: 70
Removed: 70
Added: 27
Removed: 27

Uma interface java.util.concurrent.ConcurrentMap é uma subinterface da interface Map, oferece suporte a operações atômicas na variável de mapa subjacente. Ele possui métodos get e set que funcionam como leituras e gravações em variáveis ​​voláteis. Ou seja, um conjunto tem um relacionamento acontece antes com qualquer obtenção subsequente na mesma variável. Esta interface garante segurança de thread e garantias de atomicidade.

Métodos ConcurrentMap

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

default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

Tenta calcular um mapeamento para a chave especificada e seu valor mapeado atual (ou nulo se não houver mapeamento atual).

2

default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)

Se a chave especificada ainda não estiver associada a um valor (ou estiver mapeada para nulo), tenta calcular seu valor usando a função de mapeamento fornecida e o insere neste mapa, a menos que seja nulo.

3

default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

Se o valor da chave especificada estiver presente e não for nulo, tenta calcular um novo mapeamento, dada a chave e seu valor mapeado atual.

4

default void forEach(BiConsumer<? super K,? super V> action)

Executa a ação fornecida para cada entrada neste mapa até que todas as entradas tenham sido processadas ou a ação lance uma exceção.

5

default V getOrDefault(Object key, V defaultValue)

Retorna o valor para o qual a chave especificada está mapeada, ou defaultValue se este mapa não contiver mapeamento para a chave.

6

default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

Se a chave especificada ainda não estiver associada a um valor ou estiver associada a nulo, associa-a ao valor não nulo fornecido.

7

V putIfAbsent(K key, V value)

Se a chave especificada ainda não estiver associada a um valor, associe-a ao valor fornecido.

8

boolean remove(Object key, Object value)

Remove a entrada de uma chave apenas se atualmente mapeada para um determinado valor.

9

V replace(K key, V value)

Substitui a entrada por uma chave apenas se atualmente mapeada para algum valor.

10

boolean replace(K key, V oldValue, V newValue)

Substitui a entrada de uma chave apenas se atualmente mapeada para um determinado valor.

11

default void replaceAll(BiFunction<? super K,? super V,? extends V> function)

Substitui o valor de cada entrada pelo resultado de invocar a função fornecida naquela entrada até que todas as entradas tenham sido processadas ou a função lance uma exceção.

Exemplo

O programa TestThread a seguir mostra o uso de ConcurrentMap versus HashMap.

import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TestThread {

   public static void main(final String[] arguments) {
      Map<String,String> map = new ConcurrentHashMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial ConcurrentHashMap: " + map);
      Iterator<String> iterator = map.keySet().iterator();

      try { 
         
         while(iterator.hasNext()) {
            String key = iterator.next();
            
            if(key.equals("3")) {
               map.put("4", "Four");
            }
         }
      } catch(ConcurrentModificationException cme) {
         cme.printStackTrace();
      }
      System.out.println("ConcurrentHashMap after modification: " + map);

      map = new HashMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial HashMap: " + map);
      iterator = map.keySet().iterator();

      try {
         
         while(iterator.hasNext()) {
            String key = iterator.next();
            
            if(key.equals("3")) {
               map.put("4", "Four");
            }
         }
         System.out.println("HashMap after modification: " + map);
      } catch(ConcurrentModificationException cme) {
         cme.printStackTrace();
      }
   }  
}

Isso produzirá o seguinte resultado.

Resultado

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
ConcurrentHashMap after modification: {1 = One, 2 = Two, 3 = Three, 4 = Four, 5 = Five, 6 = Six}
Initial HashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(Unknown Source)
	at java.util.HashMap$KeyIterator.next(Unknown Source)
	at TestThread.main(TestThread.java:48)

Uma interface java.util.concurrent.ConcurrentNavigableMap é uma subinterface da interface ConcurrentMap e suporta operações NavigableMap, e recursivamente para seus submapas navegáveis ​​e correspondências aproximadas.

Métodos ConcurrentMap

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

NavigableSet<K> descendingKeySet()

Retorna uma visualização NavigableSet de ordem reversa das chaves contidas neste mapa.

2

ConcurrentNavigableMap<K,V> descendingMap()

Retorna uma visualização de ordem reversa dos mapeamentos contidos neste mapa.

3

ConcurrentNavigableMap<K,V> headMap(K toKey)

Retorna uma visualização da parte deste mapa cujas chaves são estritamente menores que toKey.

4

ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive)

Retorna uma visualização da parte deste mapa cujas chaves são menores que (ou iguais a, se inclusivo for verdadeiro) toKey.

5

NavigableSet<K> keySet()

Retorna uma visualização NavigableSet das chaves contidas neste mapa.

6

NavigableSet<K> navigableKeySet()

Retorna uma visualização NavigableSet das chaves contidas neste mapa.

7

ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)

Retorna uma visualização da parte deste mapa cujas chaves variam de chave a chave chave.

8

ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey)

Retorna uma visualização da parte deste mapa cujas chaves variam de fromKey, inclusive, a toKey, exclusivo.

9

ConcurrentNavigableMap<K,V> tailMap(K fromKey)

Retorna uma visualização da parte deste mapa cujas chaves são maiores ou iguais a fromKey.

10

ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive)

Retorna uma visualização da parte deste mapa cujas chaves são maiores que (ou iguais a, se inclusivo for verdadeiro) fromKey.

Exemplo

O programa TestThread a seguir mostra o uso de ConcurrentNavigableMap.

import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

public class TestThread {

   public static void main(final String[] arguments) {
      ConcurrentNavigableMap<String,String> map =
         new ConcurrentSkipListMap<String, String>();

      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");

      System.out.println("Initial ConcurrentHashMap: "+map);
      System.out.println("HeadMap(\"2\") of ConcurrentHashMap: "+map.headMap("2"));
      System.out.println("TailMap(\"2\") of ConcurrentHashMap: "+map.tailMap("2"));
      System.out.println(
         "SubMap(\"2\", \"4\") of ConcurrentHashMap: "+map.subMap("2","4"));
   }  
}

Isso produzirá o seguinte resultado.

Resultado

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
HeadMap("2") of ConcurrentHashMap: {1 = One}
TailMap("2") of ConcurrentHashMap: {2 = Two, 3 = Three, 5 = Five, 6 = Six}
SubMap("2", "4") of ConcurrentHashMap: {2 = Two, 3 = Three}