concurrency java semaphore
Este tutorial irá discutir os componentes do pacote java.util.concurrent como Java Semaphore, Executor Framework, ExecutorService para implementar Concurrency em Java:
De nossos tutoriais Java anteriores, sabemos que a plataforma Java oferece suporte à programação simultânea desde o início. A unidade básica de simultaneidade é um thread e discutimos threads e multithreading em Java em detalhes.
Do Java 5 em diante, um pacote chamado ‘java.util.concurrent’ foi adicionado à plataforma Java. Este pacote contém um conjunto de classes e bibliotecas que torna mais fácil para o programador desenvolver aplicativos simultâneos (multithread). Usando este pacote, não temos que escrever classes complexas, pois temos implementações prontas da maioria dos conceitos concorrentes.
=> Verifique TODOS os tutoriais Java aqui.
Neste tutorial, discutiremos os vários componentes do pacote java.util.concurrent relacionados à simultaneidade e multithreading em Java.
O que você aprenderá:
como abrir um arquivo torrent
Pacote java.util.concurrent
Listados abaixo estão os vários componentes do pacote java.util.concurrent relacionados à simultaneidade e multithreading em Java. Vamos explorar cada componente em detalhes com a ajuda de exemplos de programação simples. Alguns dos componentes que iremos
discutir são:
- Estrutura executora
- ExecutorService
- Grupo de discussão
- Callable
- Locks- ReentrantLock
- Semáforo
- ForkJoinPool
Executor Framework em Java
O Executor Framework em Java foi lançado com o JDK 5. O Executor Framework (java.util.concurrent.Executor) é um framework que consiste em componentes que nos ajudam a lidar eficientemente com múltiplos threads.
Usando o Executor Framework, podemos executar objetos que são Executáveis reutilizando os threads já existentes. Não precisamos criar novos threads sempre que precisarmos executar objetos.
A API do Executor separa ou desacopla a execução de uma tarefa da tarefa real usando um Executor . Um executor é centrado na interface do Executor e tem subinterfaces, ou seja, ExecutorService e a classe ThreadPoolExecutor.
Assim, usando o Executor, basta criar objetos Runnable e enviá-los ao executor que os executa.
Algumas das melhores práticas a serem seguidas ao usar a estrutura do Executor são,
- Devemos verificar e planejar um código para revisar as listas principais para que possamos detectar deadlock, bem como livelock no código.
- O código Java deve sempre ser executado em ferramentas de análise estática. Exemplos das ferramentas de análise estática são FindBugs e PMD.
- Devemos não apenas capturar exceções, mas também erros em programas multithread.
Agora vamos discutir os componentes do Executor Framework em Java.
Executor
O executor pode ser definido como uma interface usada para representar um objeto que executa as tarefas fornecidas a ele. Se a tarefa deve ser executada no encadeamento atual ou novo, depende do ponto de onde a chamada foi iniciada, o que depende ainda mais da implementação.
Portanto, usando o Executor, podemos separar as tarefas da tarefa real e, em seguida, executá-las de forma assíncrona.
No entanto, a execução da tarefa usando o Executor não precisa ser assíncrona. Os executores também podem invocar a tarefa instantaneamente usando o thread de invocação.
Abaixo está um exemplo de código para criar a instância do Executor:
public class Invoker implements Executor { @Override public void execute (Runnable r_interface) { r_interface.run(); } }Uma vez que o invocador é criado, conforme mostrado acima, podemos usá-lo para executar a tarefa da seguinte maneira.
public void execute () { Executor executor = new Invoker (); executor.execute ( () -> { //perform task }); }Observe que, se a tarefa não for aceita pelo Executor, ele lançará RejectedExecutionException.
ExecutorService
Um ExecutorService (java.util.concurrent.ExecutorService) agenda as tarefas enviadas de acordo com a disponibilidade de threads e também mantém uma fila de memória. O ExecutorService atua como uma solução completa para o processamento assíncrono de tarefas.
Para usar ExecutorService no código, criamos uma classe Runnable. O ExecutorService mantém um pool de threads e também atribui as tarefas às threads. As tarefas também podem ser enfileiradas caso o thread não esteja disponível.
A seguir está um exemplo simples de ExecutorService.
import java.util.concurrent.*; public class Main { public static void main(String() args) { //create ExecutorService instance with 10 threads ExecutorService executor_Service = Executors.newFixedThreadPool(10); //assign the service to Runnable instance executor_Service.execute(new Runnable() { @Override public void run() { //print the message System.out.println('Simple Example of ExecutorService!!!'); } }); //shutdown executorService executor_Service.shutdown(); } } Resultado

No programa acima, criamos uma instância ExecutorService simples com um pool de threads que consiste em 10 threads. Em seguida, ele é atribuído à instância Runnable e executado para imprimir a mensagem acima. Após imprimir a mensagem, o ExecutorService é encerrado.
Grupo de discussão
Um pool de threads em Java é um grupo de threads de trabalho que podem ser reutilizados muitas vezes e trabalhos atribuídos.
Um pool de threads contém um grupo de threads de tamanho fixo. Cada encadeamento é retirado do pool de encadeamentos e atribuído a uma tarefa pelo provedor de serviços. Assim que a tarefa atribuída for concluída, o encadeamento é fornecido ao pool de encadeamentos novamente.
O pool de threads é vantajoso, pois não precisamos criar um novo thread sempre que a tarefa estiver disponível, portanto, o desempenho é aprimorado. É usado em aplicativos de tempo real que usam Servlet e JSP, onde conjuntos de encadeamentos são usados para processar solicitações.
Em aplicações multi-threaded, o Thread Pool economiza recursos e ajuda a conter o paralelismo dentro de limites predefinidos.
O programa Java abaixo demonstra o pool de threads em Java.
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class WorkerThreadClass implements Runnable { private String message; //thread class constructor public WorkerThreadClass(String s){ this.message=s; } //run method for thread public void run() { System.out.println(' Start: '+message); processmessage(); //sleep between start and end System.out.println(' End: '+ message); } //processmessage method => sleeps the thread for 2 sec private void processmessage() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Main { public static void main(String() args) { //create a ExecutorService instance ExecutorService executor = Executors.newFixedThreadPool(5);//creating a pool of 5 threads //create thread instances and execute them for (int i = 0; i <5; i++) { Runnable workerThrd = new WorkerThreadClass('Thread_' + i); executor.execute(workerThrd);//calling execute method of ExecutorService } //shutdown ExecutorService executor.shutdown(); while (!executor.isTerminated()) { } System.out.println('Finished all threads'); } } Resultado

Nos programas acima, há um pool de threads de 5 threads que são criados usando o método “newFixedThreadPool”. Em seguida, os threads são criados e adicionados ao pool e atribuídos ao ExecutorService para execução.
Chamável em Java
Já sabemos que podemos criar threads usando duas abordagens. Uma abordagem é estender a classe Thread, enquanto a segunda abordagem é implementar uma interface Runnable.
No entanto, os threads criados usando a interface Runnable carecem de um recurso, ou seja, não retorna um resultado quando o thread é encerrado ou run () conclui a execução. É aqui que a interface Callable entra em cena.
Usando uma interface Callable, definimos uma tarefa para que ela retorne um resultado. Também pode lançar uma exceção. A interface Callable é uma parte do pacote java.util.concurrent.
A interface Callable fornece um método call () que está nas linhas semelhantes ao método run () fornecido pela interface Runnable, com a única diferença de que o método call () retorna um valor e lança uma exceção marcada.
O método call () da interface Callable possui o seguinte protótipo.
public Object call () throws Exception; Como o método call () retorna um Object, o thread principal deve estar ciente disso.
Portanto, o valor de retorno deve ser armazenado em outro objeto conhecido pelo thread principal. Esse propósito é servido usando um objeto “Futuro”. Um objeto Future é um objeto que contém o resultado retornado por um thread. Ou em outras palavras, ele manterá o resultado quando Callable retornar.
Callable encapsula uma tarefa que deve ser executada em outro thread. Um objeto Future armazena o resultado retornado de um thread diferente.
Uma interface chamável não pode ser usada para criar um encadeamento. Precisamos do Runnable para criar um thread. Então, para armazenar o resultado, um objeto Future é necessário. Java fornece um tipo concreto denominado “FutureTask” que combina a funcionalidade implementando Runnable e Future.
Criamos uma FutureTask fornecendo um construtor com Callable. Este objeto FutureTask é então fornecido ao construtor da classe Thread para criar um objeto Thread.
A seguir, é fornecido um programa Java que demonstra a interface Callable e o objeto Future. Também usamos o objeto FutureTask neste programa.
Como já mencionado, no programa criamos uma classe que implementa uma interface Callable com um método call () substituído. No método principal, criamos 10 objetos FutureTask. Cada construtor de objeto tem um objeto de classe Callable como seu argumento. Em seguida, o objeto FutureTask é associado a uma instância de thread.
Portanto, indiretamente, criamos um thread usando um objeto de interface Callable.
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; //create a class implementing Callable interface class CallableDemo implements Callable { //define call () method public Object call() throws Exception { Random generator = new Random(); Integer randomNumber = generator.nextInt(10); Thread.sleep(randomNumber * 1000); return randomNumber; } } public class Main { public static void main(String() args) throws Exception { // Array of FutureTask objects FutureTask() randomNumberTasks = new FutureTask(10); for (int i = 0; i <10; i++) { Callable callable = new CallableDemo(); // Create the FutureTask with Callable class randomNumberTasks(i) = new FutureTask(callable); // create thread with FutureTask Thread t = new Thread(randomNumberTasks(i)); //start the thread t.start(); } System.out.println('The contents of FutureTask objects:'); for (int i = 0; i < 10; i++) { // get() contents of FutureTask System.out.print(randomNumberTasks(i).get() + ' '); } } } Resultado

Conforme mostrado no programa acima, o método call () de Callable, que é substituído na classe que implementa Callable, gera números aleatórios. Assim que o thread é iniciado, ele exibe esses números aleatórios.
Além disso, usamos objetos FutureTask na função principal. Como ela implementa a interface Future, não precisamos armazenar os resultados nos objetos Thread. Da mesma forma, podemos cancelar a tarefa, verificar se ela está em execução ou concluída e também obter o resultado usando o objeto FutureTask.
ReentrantLock em Java
Discutimos a sincronização de threads usando a palavra-chave synchronized em detalhes em nosso último tutorial. O uso da palavra synchronized para sincronização de thread é o método básico e é um tanto rígido.
Usando a palavra-chave synchronized, um thread pode ser bloqueado apenas uma vez. Além disso, depois que um thread sai do bloco sincronizado, o próximo thread assume o bloqueio. Não há fila de espera. Esses problemas podem causar a inanição de algum outro thread, pois ele pode não conseguir acessar os recursos por um longo tempo.
Para resolver esses problemas, precisamos de um método flexível de sincronização dos threads. O “Reentrant Locks” é este método em Java que fornece sincronização com muito maior flexibilidade.
A classe “ReentrantLock” implementa bloqueios Reentrant e faz parte do pacote “import java.util.concurrent.locks”. A classe ReentrantLock fornece a sincronização de método para acessar recursos compartilhados. As classes também têm os métodos de bloqueio e desbloqueio para bloquear / desbloquear recursos quando acessados por threads.
Um recurso peculiar do ReentrantLock é que o thread pode bloquear o recurso compartilhado mais de uma vez usando ReentrantLock. Ele fornece a contagem de espera, que é definida como um quando a thread bloqueia o recurso.
O thread pode entrar novamente e acessar o recurso antes de desbloquear. Cada vez que a thread acessa o recurso usando o bloqueio Reentrant, a contagem de espera é incrementada em um. Para cada desbloqueio, a contagem de espera é diminuída em um.
Quando a contagem de espera chega a 0, o recurso compartilhado é desbloqueado.
A classe ReentrantLock também fornece um parâmetro de justiça que é um valor booleano que pode ser passado com o construtor do bloqueio. Quando o parâmetro fairness é definido como true, sempre que um thread libera o bloqueio, o bloqueio é passado para o thread de espera mais longa. Isso evita a fome.
Os bloqueios reentrantes podem ser usados da seguinte forma:
return_type method_name() { reentrantlock.lock(); try { //Do some work } catch(Exception e) { e.printStackTrace(); } finally { reentrantlock.unlock(); } } Observe que a instrução de desbloqueio para ReentrantLock está sempre no bloco finally. Isso garante que o bloqueio seja liberado, mesmo se uma exceção for lançada.
Vamos implementar um programa Java para entender o ReentrantLock.
import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; //thread class that implements Runnable interface class ThreadClass implements Runnable { String task_name; //define ReentrantLock object ReentrantLock thrd_lck; //ThreadClass constructor initialized lock and task name public ThreadClass(ReentrantLock r_lock, String t_name) { thrd_lck = r_lock; task_name = t_name; } //thread run () method public void run() { boolean bool_val = false; while (!bool_val) { //check for Outer Lock boolean tryLock_val = thrd_lck.tryLock(); // if lock is free, do the following if(tryLock_val) { try { for(int i=0;i<=6;i++) { if(i>=2) { thrd_lck.lock(); Thread thread_one = new Thread(); System.out.println('Thread Created.....'); if(i==3) { thread_one.setName('Maint Thread2'); System.out.println('Thread Created.....'); } } if(i==4) thrd_lck.unlock(); break; } System.out.println('ReentrantLock=>Is locked after sleep(1500) : ' + thrd_lck.isLocked()); System.out.println('Work done for task : ' + task_name ); bool_val = true; } catch(Exception e) { e.printStackTrace(); } } } } } public class Main { public static void main(String() args) { //define ReentrantLock lock object and service pool ReentrantLock reentrant_lock = new ReentrantLock(); ExecutorService pool = Executors.newFixedThreadPool(2); //create thread instance and pass lock and task name Runnable worker_thread = new ThreadClass(reentrant_lock, 'ThreadJob'); //execute the thread in exec pool pool.execute(worker_thread); //shut down the pool pool.shutdown(); } } Resultado

No programa acima, criamos um thread e usamos o ReentrantLock para ele. Usando ReentrantLock, o recurso compartilhado pode ser acessado.
Semáforo em Java
O próximo método de sincronização de thread é usando o Semaphore. Usando esta construção chamada semáforo, o acesso a um recurso compartilhado é controlado por meio de um contador. Os sinais são enviados entre os fios para que possamos proteger a seção crítica e também evitar sinais perdidos.
Um semáforo pode ser definido como uma variável usada para gerenciar processos simultâneos por meio da sincronização desses processos. Os semáforos também são usados para sincronizar o acesso ao recurso compartilhado e, assim, evitar uma condição de corrida. A permissão dada a um thread para acessar o recurso compartilhado por semáforo também é chamada de permissão.
Dependendo das funções que executam, os semáforos podem ser divididos em dois tipos:
# 1) Semáforo binário: Um semáforo binário é usado para sincronizar processos simultâneos e implementar exclusão mútua. Um semáforo binário assume apenas dois valores, ou seja, 0 e 1.
# 2) Semáforo de contagem: O semáforo de contagem possui um valor que indica a quantidade de processos que podem entrar na seção crítica. Em qualquer ponto, o valor indica o número máximo de processos que entram na seção crítica.
Então, como funciona um Semaphore?
O funcionamento de um semáforo pode ser resumido nas seguintes etapas:
- Se a contagem do semáforo for> 0, significa que a thread tem permissão para acessar a seção crítica e, em seguida, a contagem é diminuída.
- Caso contrário, o thread é bloqueado até que a licença seja adquirida.
- Quando a thread termina de acessar o recurso compartilhado, a permissão é liberada e a contagem do semáforo é incrementada para que outra thread possa repetir as etapas acima e adquirir a licença.
As etapas acima do funcionamento dos semáforos podem ser resumidas no fluxograma abaixo.

Em Java, não precisamos implementar nosso semáforo, mas ele fornece um Semáforo classe que implementa a funcionalidade do semáforo. A classe Semaphore é uma parte do java.util.concurrent pacote.
A classe Semaphore fornece os seguintes construtores, usando os quais podemos criar um objeto semáforo:
Semaphore (int num_value) Semaphore (int num_value, boolean how) Aqui,
num_value => valor inicial da contagem de licenças que determina o número de threads que podem acessar o recurso compartilhado.
como => define a ordem em que os threads receberão permissões (como = verdadeiro). Se how = false, essa ordem não é seguida.
Agora implementaremos um programa Java que demonstrará o Semaphore usado para gerenciar o acesso a recursos compartilhados e evitar a condição de corrida.
import java.util.concurrent.*; //class for shared resource class SharedRes { static int count = 0; } class ThreadClass extends Thread { Semaphore sem; String threadName; public ThreadClass(Semaphore sem, String threadName) { super(threadName); this.sem = sem; this.threadName = threadName; } @Override public void run() { // Thread T1 processing if(this.getName().equals('T1')) { System.out.println('Start: ' + threadName); try { System.out.println(threadName + ' :waiting for a permit.'); // acquire the permit sem.acquire(); System.out.println(threadName + ':Acquired permit'); // access shared resource for(int i=0; i <5; i++) { SharedRes.count++; System.out.println(threadName + ': ' + SharedRes.count); Thread.sleep(10); } } catch (InterruptedException exc) { System.out.println(exc); } // Release the permit. System.out.println(threadName + ':Released the permit'); sem.release(); } // Thread T2 processing else { System.out.println('Start: ' + threadName); try { System.out.println(threadName + ':waiting for a permit.'); // acquire the lock sem.acquire(); System.out.println(threadName + ':Acquired permit'); // process the shared resource for(int i=0; i < 5; i++) { SharedRes.count--; System.out.println(threadName + ': ' + SharedRes.count); Thread.sleep(10); } } catch (InterruptedException exc) { System.out.println(exc); } // Release the permit. System.out.println(threadName + ':Released the permit.'); sem.release(); } } } public class Main { public static void main(String args()) throws InterruptedException { //create Semaphore=> #permits = 1 Semaphore sem = new Semaphore(1); // Create thread instances T1 & T2 //T1=> Increments the count; T2=> Decrements the count ThreadClass thread1 = new ThreadClass(sem, 'T1'); ThreadClass thread2 = new ThreadClass(sem, 'T2'); // start T1 & T2 thread1.start(); thread2.start(); // Wait T1 & T2 thread1.join(); thread2.join(); System.out.println('count: ' + SharedRes.count); // display final count. } } Resultado

Este programa declarou uma classe para o recurso compartilhado. Ele também declara uma classe de thread na qual temos uma variável semáforo que é inicializada no construtor da classe.
No método run () substituído da classe Thread, o processamento da instância do thread é feito no qual o thread adquire a permissão, acessa um recurso compartilhado e, em seguida, libera a permissão.
No método principal, declaramos duas instâncias de thread. Ambos os threads são iniciados e aguardam usando o método de junção. Finalmente, a contagem é exibida, ou seja, 0 indicando que ambos os threads terminaram com o recurso compartilhado.
Garfo e junte-se em Java
A estrutura fork / join foi introduzida pela primeira vez em Java 7. Essa estrutura consiste em ferramentas que podem acelerar o processamento paralelo. Ele usa todos os núcleos de processador disponíveis no sistema e conclui a tarefa. A estrutura fork / join usa a abordagem de dividir e conquistar.
A ideia básica por trás da estrutura Fork / Join é que a primeira estrutura “Forks”, ou seja, divide recursivamente a tarefa em subtarefas individuais menores até que as tarefas sejam atômicas para que possam ser executadas de forma assíncrona.
Depois de fazer isso, as tarefas são 'unidas', ou seja, todas as subtarefas são unidas recursivamente em uma única tarefa ou valor de retorno.
A estrutura fork / join tem um pool de threads conhecido como “ForkJoinPool”. Este pool gerencia o tipo “ForkJoinWorkerThread” de threads de trabalho, fornecendo assim um processamento paralelo eficaz.
ForkJoinPool gerencia os threads de trabalho e também nos ajuda a obter informações sobre o desempenho e o estado do pool de threads. O ForkJoinPool é uma implementação do “ExecutorService” que discutimos acima.
Ao contrário dos threads de trabalho, o ForkJoinPool não cria um thread separado para cada subtarefa. Cada thread no ForkJoinPool mantém seu deque (fila dupla) para armazenar tarefas.
O deque atua como balanceamento da carga de trabalho do thread e faz isso com a ajuda de um 'algoritmo de roubo de trabalho' descrito abaixo.
Algoritmo de roubo de trabalho
Podemos definir o algoritmo de roubo de trabalho em palavras simples como “Se um tópico estiver livre,‘ roube ’o trabalho dos tópicos ocupados”.
Um thread de trabalho sempre obterá as tarefas de seu deque. Quando todas as tarefas no deque estiverem esgotadas e o deque estiver vazio, o thread de trabalho assumirá uma tarefa da cauda de outro deque ou da 'fila de entrada global'.
Desta forma, a possibilidade de threads competindo por tarefas é minimizada e também o número de vezes que a thread precisa procurar trabalho é reduzida. Isso ocorre porque o tópico já obteve a maior parte do trabalho disponível e o concluiu.
Então, como podemos usar o ForkJoinPool em um programa?
A definição geral de ForkJoinPool é a seguinte:
public class ForkJoinPool extends AbstractExecutorServiceA classe ForkJoinPool faz parte do pacote “java.util.concurrent”.
No Java 8, criamos uma instância do ForkJoinPool usando seu método estático “common-pool ()” que fornece uma referência ao pool comum ou ao pool de threads padrão.
ForkJoinPool commonPool = ForkJoinPool.commonPool ();No Java 7, criamos uma instância ForkJoinPool e a atribuímos ao campo da classe de utilitário conforme mostrado abaixo.
public static ForkJoinPool forkJoinPool = new ForkJoinPool(2);A definição acima indica que o pool tem um nível de paralelismo de 2, de forma que o pool usará 2 núcleos de processador.
Para acessar o pool acima, podemos fornecer a seguinte declaração.
ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool;O tipo básico para tarefas ForkJoinPool é “ForkJoinTask”. Devemos estender uma de suas subclasses, ou seja, para tarefas nulas, a RecursiveAction e para tarefas que retornam um valor, a RecursiveTask. Ambas as classes estendidas fornecem um método abstrato compute () no qual definimos a lógica da tarefa.
Dada a seguir é um exemplo para demonstrar o ForkJoinPool.
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; //class declaration for ForkJoinPool tasks class FJPoolTask extends RecursiveAction { private long Load = 0; public FJPoolTask(long Load) { this.Load = Load; } @Override protected void compute() { //if threshold is reached, break tasks into smaller tasks List subtasks = new ArrayList(); subtasks.addAll(createSubtasks()); for(RecursiveAction subtask : subtasks){ subtask.fork(); } } //create subtasks private List createSubtasks() { List sub_tasks =new ArrayList(); FJPoolTask sub_task1 = new FJPoolTask(this.Load / 2); FJPoolTask sub_task2 = new FJPoolTask(this.Load / 2); FJPoolTask sub_task3 = new FJPoolTask(this.Load / 2); sub_tasks.add(sub_task1); sub_tasks.add(sub_task2); sub_tasks.add(sub_task3); return sub_tasks; } } public class Main { public static void main(final String() arguments) throws InterruptedException { //get count of available processors int proc = Runtime.getRuntime().availableProcessors(); System.out.println('Processors available:' +proc); //declare forkJoinPool ForkJoinPool Pool = ForkJoinPool.commonPool(); System.out.println(' Active Threads (Before invoke):' +Pool.getActiveThreadCount()); //Declare ForkJoinPool task object FJPoolTask t = new FJPoolTask(400); //submit the tasks to the pool Pool.invoke(t); System.out.println(' Active Threads (after invoke):' +Pool.getActiveThreadCount()); System.out.println('Common Pool Size :' +Pool.getPoolSize()); } } Resultado

No programa acima, encontramos o número de threads ativas no sistema antes e depois de chamar o método “invoke ()”. O método invoke () é usado para enviar as tarefas ao pool. Também encontramos o número de núcleos de processador disponíveis no sistema.
perguntas frequentes
P # 1) O que é Java Util Concurrent?
Responda: O pacote “java.util.concurrent” é um conjunto de classes e interfaces fornecidas por Java para facilitar o desenvolvimento de aplicativos concorrentes (multi-thread). Usando este pacote, podemos usar diretamente a interface e as classes, bem como APIs, sem ter que escrever nossas classes.
P # 2) Quais das seguintes são implementações simultâneas presentes no java.util. pacote simultâneo?
Responda: Em um alto nível, o pacote java.util.concurrent contém utilitários como Executors, Synchronizers, Queues, Timings e Concurrent Collections.
P # 3) O que é Future Java?
Responda: Um objeto Future (java.util.concurrent.Future) é usado para armazenar o resultado retornado por um encadeamento quando a interface Callable é implementada.
P # 4) O que é thread-safe em Java?
Responda: Um código ou classe thread-safe em Java é um código ou classe que pode ser compartilhado em um ambiente multi-thread ou simultâneo sem problemas e produz os resultados esperados.
P # 5) O que é coleção sincronizada em Java?
Responda: Uma coleção sincronizada é uma coleção thread-safe. O método synchronized collection () da classe java.util.Collections retorna uma coleção sincronizada (thread-safe).
Conclusão
Com este tutorial, concluímos o tópico de multithreading e simultaneidade em Java. Discutimos o multithreading em detalhes em nossos tutoriais anteriores. Aqui, discutimos a simultaneidade e a implementação relacionada à simultaneidade e multithreading que fazem parte do pacote java.util.concurrent.
Discutimos mais dois métodos de sincronização, semáforos e ReentrantLock. Também discutimos o ForkJoinPool que é usado para executar as tarefas, dividindo-as em tarefas mais simples e, em seguida, unindo o resultado.
O pacote java.util.concurrent também oferece suporte à estrutura Executor e aos executores que nos ajudam a executar threads. Também discutimos a implementação do pool de threads que consiste em threads reutilizáveis que são retornados ao pool quando a execução é concluída.
Discutimos outra interface semelhante a Runnable, que também nos ajuda a retornar um resultado do encadeamento e o objeto Future usado para armazenar o resultado do encadeamento obtido.
=> Veja a série de treinamento simples em Java aqui.
Leitura recomendada
- Thread.Sleep () - Método Thread Sleep () em Java com exemplos
- Implantação Java: Criação e execução de arquivo JAR Java
- Java Basics: Java Syntax, Java Class e Core Java Concepts
- Java Virtual Machine: como a JVM ajuda na execução de aplicativos Java
- Modificadores de acesso em Java - Tutorial com exemplos
- Java sincronizado: o que é sincronização de thread em Java
- Tutorial JAVA para iniciantes: mais de 100 tutoriais práticos em vídeo Java
- Java Integer e classe Java BigInteger com exemplos