Mutex em Java

Haider Ali 15 fevereiro 2024
  1. Threading e Multithreading
  2. Threads em Java
  3. Mutex
  4. Explicação
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.

exemplo java mutex

O tamanho continuará aumentando à medida que o programa é executado.

Autor: Haider Ali
Haider Ali avatar Haider Ali avatar

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

Artigo relacionado - Java Threading