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.