Mutex em Java
No mundo da ciência da computação, a exclusão mútua ou Mutex é conhecida como uma propriedade de controle de concorrência. Cada computador trabalha a menor sequência de instrução programada conhecida como thread. Ao mesmo tempo, o computador funciona em um único segmento. Vamos mergulhar em mais alguns aspectos para um melhor entendimento.
Threading e Multithreading
CPU funciona em threads para multitarefa. Cada processo funciona mudando constantemente de thread para thread em uma velocidade muito rápida. Por exemplo, quando assistimos a um vídeo, o áudio do vídeo está em uma thread diferente e a imagem em outro. A troca constante entre esses dois é muito rápida e é conhecida como multithreading.
Threads em Java
A criação de um thread em Java é feita estendendo uma classe e implementando uma interface. Multithreading é um recurso Java que permite a execução de duas ou mais partes de um programa simultaneamente para maximizar a eficiência da CPU. Um thread é um componente de tal programa. Threads são, portanto, processos leves dentro de processos.
Mutex
Dois ou mais threads podem precisar acessar um recurso compartilhado simultaneamente em um programa multithread, resultando em um comportamento inesperado. Estruturas de dados, dispositivos de entrada e saída, arquivos e conexões de rede são exemplos de recursos compartilhados.
É referido como uma condição de corrida. A seção principal do programa é a parte do programa que acessa o recurso compartilhado. Como resultado, devemos sincronizar o acesso à parte crítica para evitar uma condição de corrida.
O tipo mais básico de sincronizador é um mutex (ou exclusão mútua), que garante que apenas um thread possa executar a área essencial de um programa de computador por vez. É implementado por uma classe chamada semáforo
.
Um thread obtém o mutex, acessa a seção crucial e, por fim, libera o mutex para acessar uma região crítica. Enquanto isso, todos os outros threads são bloqueados até que o mutex seja liberado. Um segmento pode entrar na seção crítica assim que sair da área crítica.
Para mutex, existem dois métodos de bloqueio e desbloqueio. Eles são conhecidos como acquire()
e release()
respectivamente. Agora dê uma olhada no exemplo abaixo.
Saiba mais sobre Mutex aqui.
import java.util.LinkedList; // linked list import
import java.util.concurrent.Semaphore; // semaphore import
public class Mutex {
static LinkedList<String> WorkingQueue = new LinkedList<String>();
// track the record of works
static Semaphore mutex1 = new Semaphore(0); // creating a Semaphore To ImplementLogic
static Semaphore mutex = new Semaphore(1); // Creating A Mutex
}
No exemplo acima, fizemos dois objetos Mutex com o nome de mutex
e mutex1
. Estaremos usando mutex1
para controlar a alternância entre dois tópicos. A razão para criar a lista vinculada é ter um histórico de threads. Agora, vamos adicionar dois threads no código acima. Dois tópicos com o nome de Producer
e Consumer
.
import java.util.LinkedList; // linked list import
import java.util.concurrent.Semaphore; // semaphore import
public class Mutex {
static LinkedList<String> WorkingQueue = new LinkedList<String>();
// track the record of works
static Semaphore mutex1 = new Semaphore(0); // creating a Semaphore To ImplementLogic
static Semaphore mutex = new Semaphore(1); // Creating A Mutex
static class Producer extends Thread {
public void run() { // default run method of thread
int counter = 1;
try {
while (true) {
String threadName = Thread.currentThread().getName()
+ counter++; // counter is added to have the thread number being used
mutex.acquire(); // Acquiring Lock before Producing so the consumer cannot consume.
WorkingQueue.add(threadName);
System.out.println("Producer is prdoucing producing: " + threadName);
mutex.release(); // releasing After Production ;
mutex1.release(); // relesing lock for consumer...so consumer can consume after production
Thread.sleep(2000); // just to Reduce the Execution Speed
}
} catch (Exception e) { /*nothing */
}
}
}
static class Consumer extends Thread {
String consumerName;
public Consumer(String name) {
this.consumerName = name;
}
public void run() {
try {
while (true) {
mutex1.acquire(); /// Again Acquiring So no production while consuming
mutex.acquire(); // Acquring Other consumers lock one consume at one time
String result = "";
for (String value : WorkingQueue) {
result = value + ",";
}
System.out.println(consumerName + " consumes value: " + result
+ "Total Size working Queue Size " + WorkingQueue.size() + "\n");
mutex.release(); // releasing lock for other consumers.
}
} catch (Exception e) {
}
}
public static void main(String[] args) {
Producer producer = new Producer();
producer.start();
Consumer c1 = new Consumer("Bill Gates");
Consumer c2 = new Consumer("Jeff Bezoz");
Consumer c3 = new Consumer("Mark Zukerberg");
c1.start();
c2.start();
c3.start();
}
}
}
Explicação
O código acima também é autoexplicativo, mas esta explicação resolverá a confusão.
Dentro do Tópico Producer
Quando você executa o programa acima, ele cria um encadeamento producer
. Dentro desse thread, há um loop while
que será executado por vezes indefinidas. A string threadName
é apenas para exibir a execução da thread. O objeto mutex
irá adquirir o bloqueio para que a thread do consumidor seja funcional. (O principal objetivo do Mutex, obter o controle da concorrência).
Depois disso, o encadeamento producer
torna-se funcional. Então, temos que liberar este segmento para produção. No thread producer
, vamos liberar mutex1
, o objeto responsável por manipular a troca entre consumer
e producer
. Após o lançamento, os consumidores começarão a consumir, ou seja, o segmento consumer
estará funcional.
Por dentro do Thread Consumer
Logo após entrarmos no segmento consumer
, adquirimos mutex1
para interromper a produção durante o consumo. Como você pode ver, criamos três consumidores sob os nomes C1
, C2
e C3
. Para permitir que um consumidor seja funcional ao mesmo tempo, também adquirimos o mutex
.
Depois disso, C1
ficará funcional, enquanto C2
e C3
serão adquiridos. Após a conclusão, mutex
será lançado novamente, permitindo que o outro consumidor seja funcional.
É assim que o mutex funciona em Java. Depois de executar o programa acima. Ele mostrará constantemente o número atual de encadeamentos do producer
em uso e o nome do consumer
que o utiliza.
O tamanho continuará aumentando à medida que o programa é executado.
Haider specializes in technical writing. He has a solid background in computer science that allows him to create engaging, original, and compelling technical tutorials. In his free time, he enjoys adding new skills to his repertoire and watching Netflix.
LinkedIn