Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprenda Lock e Condition | Mecanismos de Sincronização de Alto Nível
Multithreading em Java

bookLock e Condition

Em Java, a sincronização padrão é baseada na palavra-chave synchronized e em objetos monitor incorporados. No entanto, em alguns casos, o synchronized pode não ser suficiente, especialmente quando é necessária maior flexibilidade no gerenciamento de threads.

Descrição Geral

As interfaces Lock e Condition, introduzidas no pacote java.util.concurrent.locks, oferecem recursos avançados de gerenciamento de threads.

Nesta imagem, é possível observar que a primeira thread adquire o lock utilizando o método lock(), e nesse momento, outra thread não pode adquirir o mesmo lock. Assim que todo o código dentro do lock for executado, será chamado o método unlock() para liberar o lock. Somente após isso a segunda thread poderá adquirir o lock.

Diferença

A diferença entre essas duas interfaces é que as implementações de Lock são uma alternativa de alto nível ao bloco synchronized, e as implementações da interface Condition são uma alternativa aos métodos notify()/wait(). Ambas as interfaces fazem parte do pacote java.util.concurrent.locks.

Exemplos do Mundo Real

Imagine o gerenciamento de uma fila para inscrição em um evento. Para evitar excesso de inscrições e garantir a alocação adequada de assentos, é necessário utilizar mecanismos de bloqueio e condições para manter o fluxo de novas inscrições aguardando até que uma vaga esteja disponível.

Classe ReentrantLock

A classe ReentrantLock do pacote java.util.concurrent.locks é uma implementação da interface Lock. Ela fornece funcionalidades para o gerenciamento explícito de locks.

Principais métodos de ReentrantLock:

  • lock(): Captura um lock;
  • unlock(): Libera o lock;
  • tryLock(): Tenta capturar o lock e retorna true se a captura for bem-sucedida;
  • tryLock(long timeout, TimeUnit unit): Tenta capturar o lock pelo tempo especificado;
  • newCondition(): Cria uma condição para o Lock atual.
Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031
package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }

Como pode ser observado, no método increment() utilizamos o bloqueio com Lock. Quando uma thread entra no método, ela adquire o bloqueio em lock.lock(), executa o código e, em seguida, no bloco finally, liberamos o bloqueio em lock.unlock() para que outras threads possam acessar.

Liberamos o bloqueio no bloco finally por um motivo: este bloco é quase sempre executado, mesmo em casos de exceções, exceto quando o programa é finalizado.

Interface Condition

Só é possível criar um Condition vinculado a uma implementação específica de Lock. Por esse motivo, os métodos de Condition afetarão apenas o bloqueio daquela implementação específica de Lock.

Main.java

Main.java

copy
12
private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();

Principais métodos de Condition:

  • await(): Aguarda um sinal de outra thread;
  • signal(): Desbloqueia uma thread aguardando em uma condição;
  • signalAll(): Desbloqueia todas as threads aguardando na condição.
Main.java

Main.java

copy
12345678910111213141516
private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }

O método waitForCondition() bloqueia a thread até que a variável ready se torne true, o que indica que a condição foi atendida. Quando a condição é atendida, a thread continua em execução, exibindo a mensagem “Condition met!”

Note
Nota

Quando o método await() é chamado, a thread é pausada e também libera o lock que capturou. Quando a thread acorda, ela deve capturar o lock novamente e só então começará a executar!

Exemplo de Código

Agora vamos analisar um exemplo de uso de ReentrantLock e Condition para gerenciar o registro em um evento:

Um trecho curto do vídeo

Bloqueio com ReentrantLock: O método register() adquire o bloqueio com lock.lock() para evitar que múltiplas threads executem o código ao mesmo tempo.

Condição com Condition: Se não houver espaços disponíveis, a thread chama spaceAvailable.await() para aguardar até que haja espaço disponível.

Liberação do bloqueio: Quando uma thread libera espaço usando o método cancel(), ela chama spaceAvailable.signalAll() para notificar todas as threads em espera.

Tratamento de exceções: O uso de blocos try-finally garante que o lock seja liberado mesmo que ocorra uma exceção.

Note
Nota

O uso de Lock e Condition em Java permite um controle mais flexível sobre threads e sincronização do que o mecanismo tradicional de synchronized. Isso é especialmente útil em cenários complexos onde é necessário um controle mais preciso sobre threads e condições de espera.

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 3. Capítulo 1

Pergunte à IA

expand

Pergunte à IA

ChatGPT

Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo

Awesome!

Completion rate improved to 3.33

bookLock e Condition

Deslize para mostrar o menu

Em Java, a sincronização padrão é baseada na palavra-chave synchronized e em objetos monitor incorporados. No entanto, em alguns casos, o synchronized pode não ser suficiente, especialmente quando é necessária maior flexibilidade no gerenciamento de threads.

Descrição Geral

As interfaces Lock e Condition, introduzidas no pacote java.util.concurrent.locks, oferecem recursos avançados de gerenciamento de threads.

Nesta imagem, é possível observar que a primeira thread adquire o lock utilizando o método lock(), e nesse momento, outra thread não pode adquirir o mesmo lock. Assim que todo o código dentro do lock for executado, será chamado o método unlock() para liberar o lock. Somente após isso a segunda thread poderá adquirir o lock.

Diferença

A diferença entre essas duas interfaces é que as implementações de Lock são uma alternativa de alto nível ao bloco synchronized, e as implementações da interface Condition são uma alternativa aos métodos notify()/wait(). Ambas as interfaces fazem parte do pacote java.util.concurrent.locks.

Exemplos do Mundo Real

Imagine o gerenciamento de uma fila para inscrição em um evento. Para evitar excesso de inscrições e garantir a alocação adequada de assentos, é necessário utilizar mecanismos de bloqueio e condições para manter o fluxo de novas inscrições aguardando até que uma vaga esteja disponível.

Classe ReentrantLock

A classe ReentrantLock do pacote java.util.concurrent.locks é uma implementação da interface Lock. Ela fornece funcionalidades para o gerenciamento explícito de locks.

Principais métodos de ReentrantLock:

  • lock(): Captura um lock;
  • unlock(): Libera o lock;
  • tryLock(): Tenta capturar o lock e retorna true se a captura for bem-sucedida;
  • tryLock(long timeout, TimeUnit unit): Tenta capturar o lock pelo tempo especificado;
  • newCondition(): Cria uma condição para o Lock atual.
Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031
package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }

Como pode ser observado, no método increment() utilizamos o bloqueio com Lock. Quando uma thread entra no método, ela adquire o bloqueio em lock.lock(), executa o código e, em seguida, no bloco finally, liberamos o bloqueio em lock.unlock() para que outras threads possam acessar.

Liberamos o bloqueio no bloco finally por um motivo: este bloco é quase sempre executado, mesmo em casos de exceções, exceto quando o programa é finalizado.

Interface Condition

Só é possível criar um Condition vinculado a uma implementação específica de Lock. Por esse motivo, os métodos de Condition afetarão apenas o bloqueio daquela implementação específica de Lock.

Main.java

Main.java

copy
12
private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();

Principais métodos de Condition:

  • await(): Aguarda um sinal de outra thread;
  • signal(): Desbloqueia uma thread aguardando em uma condição;
  • signalAll(): Desbloqueia todas as threads aguardando na condição.
Main.java

Main.java

copy
12345678910111213141516
private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }

O método waitForCondition() bloqueia a thread até que a variável ready se torne true, o que indica que a condição foi atendida. Quando a condição é atendida, a thread continua em execução, exibindo a mensagem “Condition met!”

Note
Nota

Quando o método await() é chamado, a thread é pausada e também libera o lock que capturou. Quando a thread acorda, ela deve capturar o lock novamente e só então começará a executar!

Exemplo de Código

Agora vamos analisar um exemplo de uso de ReentrantLock e Condition para gerenciar o registro em um evento:

Um trecho curto do vídeo

Bloqueio com ReentrantLock: O método register() adquire o bloqueio com lock.lock() para evitar que múltiplas threads executem o código ao mesmo tempo.

Condição com Condition: Se não houver espaços disponíveis, a thread chama spaceAvailable.await() para aguardar até que haja espaço disponível.

Liberação do bloqueio: Quando uma thread libera espaço usando o método cancel(), ela chama spaceAvailable.signalAll() para notificar todas as threads em espera.

Tratamento de exceções: O uso de blocos try-finally garante que o lock seja liberado mesmo que ocorra uma exceção.

Note
Nota

O uso de Lock e Condition em Java permite um controle mais flexível sobre threads e sincronização do que o mecanismo tradicional de synchronized. Isso é especialmente útil em cenários complexos onde é necessário um controle mais preciso sobre threads e condições de espera.

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 3. Capítulo 1
some-alt