Synccronização em Java

O que é o Synccronização em Java?

In Java, sincronização se refere à habilidade de controlar o acesso de múltiplos threads a qualquer recurso compartilhado. É uma opção ideal quando queremos permitir que apenas um thread acesse o recurso compartilhado.

Esta abordagem de execução é geralmente chamada de programação `assíncrona`. Também existem threads nesses processadores, que são processos leves que podem executar instruções simultaneamente.

Tipos de Synchronização

Existem dois tipos de métodos de sincronização em Java:

1) Sincronização de processos

2) Sincronização de threads.

Vamos estudar Thread e Sincronização de processos em detalhe.

Sincronização de processos: Ele gerencia a sincronização entre programas. Por exemplo, programas como `Microsoft Word` e `Acrobat reader` são executados como processos individuais.

Sincronização de threads: A execução simultânea do recurso crítico por dois ou mais Threads é denominada Thread Synccronização. Você pode ser agrupado ainda em comunicação mutuamente exclusiva e entre threads.

O que é bloqueio Java?

Bloquear Java é construído em torno de uma entidade interna conhecida como monitor ou bloqueio. Todo o objeto possui um bloqueio associado a eles. Portanto, o thread que precisa de acesso consistente aos campos de um objeto deve adquirir o bloqueio do objeto antes de acessá-los e liberar o bloqueio quando o trabalho for concluído. Isso garante que apenas um thread acesse os dados compartilhados por vez.

Programa multithread com sincronização

Um programa multithread é um método ou bloco protegido contra interferência de outros threads que compartilham o mesmo recurso indicado pela palavra-chave `synchronized`.

Usando o método sincronizado

Qualquer método declarado como sincronizado é conhecido como método sincronizado. Também é usado para bloquear um objeto para qualquer recurso compartilhado. Então, quando um thread invoca um método sincronizado. Ele automaticamente toma posse do bloqueio desse objeto e o libera quando termina sua tarefa.

Nota: A palavra-chave sincronizada não pode funcionar com classes e variáveis. Somente métodos e blocos podem ser usados ​​com a palavra-chave.

Por que usar o SyncMétodo cronizado?

  • É usado para bloquear um objeto para quaisquer recursos compartilhados.
  • O objeto obtém o bloqueio sempre que o método sincronizado é chamado.
  • O bloqueio não é liberado até que o thread complete sua função

Sintaxe:

Acess_modifiers synchronized return_type method_name (Method_Parameters) {
}
class MathService {
    synchronized void getSumOfArray(int[] numbers) {
     int sum = 0;

         for (int number : numbers) {
             System.out.println(Thread.currentThread()
                     .getName()
                     + " adds "
                     + sum + " to "
                     + number + " to get -> "
                     + (sum += number));

             try {
                 Thread.sleep(500);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
         }
    }
}
public class Synchronization {
    public static void main(String[] args) {
        MathService mathService = new MathService();

        Thread threadOne = new Thread(() ->
                mathService.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadTwo = new Thread(() ->
                mathService.getSumOfArray(new int[]{20, 21, 22}));

        threadOne.start();
        threadTwo.start();
    }
}

Explicação do Código:

Execute este exemplo e observe que o thread `0` obtém primeiro o bloqueio do objeto `mathService` e usa esse bloqueio exclusivamente até concluir a execução. Os threads `0` e `1` não são intercalados neste código. A saída é mostrada abaixo.

Saída:

Thread-0 adds 0 to 10 to get -> 10
Thread-0 adds 10 to 11 to get -> 21
Thread-0 adds 21 to 12 to get -> 33
Thread-1 adds 0 to 20 to get -> 20
Thread-1 adds 20 to 21 to get -> 41
Thread-1 adds 41 to 22 to get -> 63

Usando um bloco sincronizado

Vamos supor que você não queira sincronizar o método inteiro. Em vez disso, você quer sincronizar algumas linhas de código. Naquele momento, o Syncbloco cronizado ajudou a sincronizar o selecionado Java código. Syncbloqueios de método cronizados são acessados ​​no método, enquanto bloqueios de bloco sincronizados são acessados ​​no objeto.

class MathService {
    void getSumOfArray(int[] numbers) {
        synchronized (this){
            int sum = 0;

            for (int number : numbers) {
                System.out.println(Thread.currentThread()
                        .getName()
                        + " adds "
                        + sum + " to "
                        + number + " to get -> "
                        + (sum += number));

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
public class Synchronization {
    public static void main(String[] args) {
        MathService mathService = new MathService();

        Thread threadOne = new Thread(() ->
                mathService.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadTwo = new Thread(() ->
                mathService.getSumOfArray(new int[]{20, 21, 22}));

        threadOne.start();
        threadTwo.start();
    }
}

Explicação do código:

Ao executar este código, você notará que ele funciona sem nenhuma interferência. No método synchronized, o bloqueio é aplicado pelo método, mas no bloco synchronized, o bloqueio é aplicado pelo objeto. Certifique-se de que a saída seja como mostrado abaixo.

Saída:

Thread-0 adds 0 to 10 to get -> 10
Thread-0 adds 10 to 11 to get -> 21
Thread-0 adds 21 to 12 to get -> 33
Thread-1 adds 0 to 20 to get -> 20
Thread-1 adds 20 to 21 to get -> 41
Thread-1 adds 41 to 22 to get -> 63

Explicação do código:

Ao executar este código, você notará que ele funciona sem interferências, que é o que esperávamos. No método sincronizado, o bloqueio é aplicado pelo método, mas no método de bloco sincronizado, o bloqueio é aplicado pelo objeto.

Usando sincronização estática

In Java sincronização, se houver mais de um objeto, duas threads podem adquirir os bloqueios e entrar em um bloco sincronizado ou bloco, com um bloqueio separado para cada objeto. Para evitar isso, a sincronização estática pode ser usada. Syncpalavras-chave cronizadas serão usadas antes dos métodos estáticos.

Nota: Na sincronização estática, o acesso ao bloqueio está na classe, não no objeto e método.

Código para demonstrar o problema de bloqueio de vários objetos

class MathService {
    synchronized void getSumOfArray(int[] numbers) {
            int sum = 0;

            for (int number : numbers) {
                System.out.println(Thread.currentThread()
                        .getName()
                        + " adds "
                        + sum + " to "
                        + number + " to get -> "
                        + (sum += number));

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
    }
}
public class Synchronization {
    public static void main(String[] args) {
        MathService mathService = new MathService();
        MathService mathService1 = new MathService();

        Thread threadOne = new Thread(() ->
                mathService.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadTwo = new Thread(() ->
                mathService.getSumOfArray(new int[]{20, 21, 22}));
        Thread threadThree = new Thread(() ->
                mathService1.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadFour = new Thread(() ->
                mathService1.getSumOfArray(new int[]{20, 21, 22}));

        threadOne.start();
        threadTwo.start();
        threadThree.start();
        threadFour.start();
    }
}

Explicação do código:

Ao criarmos outra instância do `MathService`, introduzimos interferência nas threads, pois elas serão intercaladas com os dois objetos. Observe que os threads `0` e os threads `2` são intercalados com os dois objetos, enquanto os threads `1` e `3` são intercalados com os dois objetos.

Saída:

Thread-0 adds 0 to 10 to get -> 10
Thread-2 adds 0 to 10 to get -> 10
Thread-0 adds 10 to 11 to get -> 21
Thread-2 adds 10 to 11 to get -> 21
Thread-0 adds 21 to 12 to get -> 33
Thread-2 adds 21 to 12 to get -> 33
Thread-1 adds 0 to 20 to get -> 20
Thread-3 adds 0 to 20 to get -> 20
Thread-1 adds 20 to 21 to get -> 41
Thread-3 adds 20 to 21 to get -> 41
Thread-1 adds 41 to 22 to get -> 63
Thread-3 adds 41 to 22 to get -> 63

Mesmo código usando método estático sincronizado

class MathService {
    synchronized static void getSumOfArray(int[] numbers) {
            int sum = 0;

            for (int number : numbers) {
                System.out.println(Thread.currentThread()
                        .getName()
                        + " adds "
                        + sum + " to "
                        + number + " to get -> "
                        + (sum += number));

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
    }
}
public class Synchronization {
    public static void main(String[] args) {
        MathService mathService = new MathService();
        MathService mathService1 = new MathService();

        Thread threadOne = new Thread(() ->
                mathService.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadTwo = new Thread(() ->
                mathService.getSumOfArray(new int[]{20, 21, 22}));
        Thread threadThree = new Thread(() ->
                mathService1.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadFour = new Thread(() ->
                mathService1.getSumOfArray(new int[]{20, 21, 22}));

        threadOne.start();
        threadTwo.start();
        threadThree.start();
        threadFour.start();
    }
}

Execute o código acima e observe que eliminamos a interferência de thread. A saída do código é mostrada abaixo.

Saída:

Thread-0 adds 0 to 10 to get -> 10
Thread-0 adds 10 to 11 to get -> 21
Thread-0 adds 21 to 12 to get -> 33
Thread-3 adds 0 to 20 to get -> 20
Thread-3 adds 20 to 21 to get -> 41
Thread-3 adds 41 to 22 to get -> 63
Thread-2 adds 0 to 10 to get -> 10
Thread-2 adds 10 to 11 to get -> 21
Thread-2 adds 21 to 12 to get -> 33
Thread-1 adds 0 to 20 to get -> 20
Thread-1 adds 20 to 21 to get -> 41
Thread-1 adds 41 to 22 to get -> 63

Vantagens de usar sincronização

Aqui estão as vantagens de trabalhar com aplicativos simultâneos:

  • O principal objetivo da sincronização em Java é evitar dados inconsistentes evitando interferência de threads.
  • A palavra-chave synchronized em Java fornece bloqueio, o que garante acesso mutuamente exclusivo ao recurso compartilhado e evita disputa de dados.
  • Também evita a reordenação de instruções de código pelo compilador, o que pode causar um problema simultâneo sutil se não usarmos palavras-chave voláteis ou sincronizadas.
  • SyncA palavra-chave hronized lê dados da memória principal do que do cache e quando libera o bloqueio.
  • Ele também libera operações de gravação da memória principal, eliminando erros de inconsistência de memória.

Desvantagens de SyncMecanismo de cronização

SyncOs mecanismos de sincronização apresentam desempenho ruim.

Por exemplo

  • Suponha que existam cinco processos, A1, A2, A3, A4 e A5.
  • Eles estão aguardando que os recursos compartilhados acessem um thread por vez.
  • Todos os processos são mantidos em espera, portanto o último da fila deve aguardar até que todos os outros processos sejam concluídos.

Resumo

  • Syncsincronização refere-se à capacidade de controlar o acesso de vários threads a qualquer recurso compartilhado.
  • Java tem dois tipos de métodos de sincronização: 1) Sincronização de processos e 2) Sincronização de threads.
  • Bloquear Java é construído em torno de uma entidade interna conhecida como monitor ou bloqueio.
  • Um programa multithread é um método ou bloco protegido da interferência de outros threads que compartilham o mesmo recurso indicado pela palavra-chave `synchronized`.
  • Qualquer método declarado como sincronizado é conhecido como método sincronizado.
  • In Java, bloqueios de método sincronizados são acessados ​​no método, enquanto bloqueios de bloco sincronizados são acessados ​​no objeto.
  • Na sincronização estática, o acesso ao bloqueio está na classe, não no objeto e método.
  • O principal objetivo da sincronização em Java é evitar dados inconsistentes evitando interferência de threads.
  • A maior desvantagem deste método é que todos os processos ficam em espera, portanto o último da fila deve esperar até que todos os outros processos sejam concluídos.