Tenho algumas perguntas sobre o uso e o significado da synchronized
palavra - chave.
- Qual é o significado da
synchronized
palavra - chave? - Quando devem ser os métodos
synchronized
? - O que significa programaticamente e logicamente?
Tenho algumas perguntas sobre o uso e o significado da synchronized
palavra - chave.
synchronized
palavra - chave?synchronized
?Respostas:
A synchronized
palavra-chave se refere a diferentes segmentos de leitura e gravação nas mesmas variáveis, objetos e recursos. Este não é um tópico trivial em Java, mas aqui está uma citação da Sun:
synchronized
Os métodos permitem uma estratégia simples para impedir a interferência do encadeamento e os erros de consistência da memória: se um objeto estiver visível para mais de um encadeamento, todas as leituras ou gravações nas variáveis desse objeto serão feitas por métodos sincronizados.
Em poucas palavras: quando você tem dois threads que estão lendo e gravando no mesmo 'recurso', digamos uma variável chamada foo
, é necessário garantir que esses threads acessem a variável de maneira atômica. Sem a synchronized
palavra - chave, seu encadeamento 1 pode não ver o encadeamento de alteração 2 feito para foo
, ou pior, pode ser apenas parcialmente alterado. Isso não seria o que você espera logicamente.
Novamente, este é um tópico não trivial em Java. Para saber mais, explore os tópicos aqui no SO e nas Interwebs sobre:
Continue explorando esses tópicos até o nome "Brian Goetz" se tornar permanentemente associado ao termo "simultaneidade" em seu cérebro.
Bem, acho que tínhamos explicações teóricas suficientes, então considere este código
public class SOP {
public static void print(String s) {
System.out.println(s+"\n");
}
}
public class TestThread extends Thread {
String name;
TheDemo theDemo;
public TestThread(String name,TheDemo theDemo) {
this.theDemo = theDemo;
this.name = name;
start();
}
@Override
public void run() {
theDemo.test(name);
}
}
public class TheDemo {
public synchronized void test(String name) {
for(int i=0;i<10;i++) {
SOP.print(name + " :: "+i);
try{
Thread.sleep(500);
} catch (Exception e) {
SOP.print(e.getMessage());
}
}
}
public static void main(String[] args) {
TheDemo theDemo = new TheDemo();
new TestThread("THREAD 1",theDemo);
new TestThread("THREAD 2",theDemo);
new TestThread("THREAD 3",theDemo);
}
}
Nota: synchronized
bloqueia a chamada do próximo segmento ao método test () desde que a execução do segmento anterior não esteja concluída. Os threads podem acessar esse método, um de cada vez. Sem synchronized
todos os threads, você pode acessar esse método simultaneamente.
Quando um thread chama o método sincronizado 'test' do objeto (aqui o objeto é uma instância da classe 'TheDemo') ele adquire o bloqueio desse objeto, qualquer novo thread não pode chamar QUALQUER método sincronizado do mesmo objeto, desde que o thread anterior que adquiriu a trava não libera a trava.
O mesmo ocorre quando qualquer método estático sincronizado da classe é chamado. O encadeamento adquire o bloqueio associado à classe (nesse caso, qualquer método sincronizado não estático de uma instância dessa classe pode ser chamado por qualquer encadeamento porque esse bloqueio no nível do objeto ainda está disponível). Qualquer outro encadeamento não poderá chamar nenhum método estático sincronizado da classe, desde que o bloqueio no nível da classe não seja liberado pelo encadeamento que atualmente mantém o bloqueio.
Saída com sincronizado
THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9
Saída sem sincronizado
THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9
synchronized
, mas a consistência da memória é ignorada.
A synchronized
palavra-chave impede o acesso simultâneo a um bloco de código ou objeto por vários encadeamentos. Todos os métodos de Hashtable
são synchronized
, portanto, apenas um encadeamento pode executar qualquer um deles por vez.
Ao usar não- synchronized
construções como HashMap
, você deve criar recursos de segurança de thread em seu código para evitar erros de consistência.
synchronized
significa que em um ambiente multiencadeado, um objeto com synchronized
método (s) / bloco (s) não permite que dois threads acessem o synchronized
(s) método (s) / bloco (s) de código ao mesmo tempo. Isso significa que um thread não pode ler enquanto outro thread o atualiza.
O segundo thread aguardará até que o primeiro thread conclua sua execução. A sobrecarga é a velocidade, mas a vantagem é a consistência garantida dos dados.
Se seu aplicativo for de thread único, synchronized
blocos não oferecem benefícios.
o synchronized
palavra-chave faz com que um encadeamento obtenha um bloqueio ao inserir o método, para que apenas um encadeamento possa executar o método ao mesmo tempo (para a instância do objeto especificada, a menos que seja um método estático).
Isso é chamado frequentemente de tornar a classe segura para threads, mas eu diria que isso é um eufemismo. Embora seja verdade que a sincronização protege o estado interno do vetor de ser corrompido, isso geralmente não ajuda muito o usuário do vetor.
Considere isto:
if (vector.isEmpty()){
vector.add(data);
}
Embora os métodos envolvidos sejam sincronizados, porque estão sendo bloqueados e desbloqueados individualmente, infelizmente dois threads cronometrados podem criar um vetor com dois elementos.
Portanto, você também precisa sincronizar o código do aplicativo.
Como a sincronização no nível do método é a) cara quando você não precisa eb) insuficiente quando precisa da sincronização, agora existem substituições não sincronizadas (ArrayList no caso de Vector).
Mais recentemente, o pacote de simultaneidade foi lançado, com vários utilitários inteligentes que cuidam de problemas com vários threads.
A palavra-chave sincronizada em Java tem a ver com segurança de encadeamento, ou seja, quando vários encadeamentos lêem ou gravam a mesma variável.
Isso pode acontecer diretamente (acessando a mesma variável) ou indiretamente (usando uma classe que usa outra classe que acessa a mesma variável).
A palavra-chave sincronizada é usada para definir um bloco de código no qual vários threads podem acessar a mesma variável de maneira segura.
Em termos de sintaxe, a synchronized
palavra-chave recebe um Object
parâmetro como é (chamado de objeto de bloqueio ), que é seguido por a { block of code }
.
Quando a execução encontra essa palavra-chave, o encadeamento atual tenta "bloquear / adquirir / possuir" (faça a sua escolha) o objeto de bloqueio e execute o bloco de código associado após a aquisição do bloqueio.
Qualquer gravação em variáveis dentro do bloco de código sincronizado é garantida para ser visível para todos os outros encadeamentos que executam código de forma semelhante dentro de um bloco de código sincronizado usando o mesmo objeto de bloqueio .
Somente um encadeamento de cada vez pode reter o bloqueio, durante o qual todos os outros encadeamentos que tentam adquirir o mesmo objeto de bloqueio aguardam (pausam sua execução). O bloqueio será liberado quando a execução sair do bloco de código sincronizado.
Adicionando synchronized
palavra-chave para uma definição do método é igual a todo o corpo do método a ser enrolado num bloco de código sincronizado com o bloqueio objeto ser this
(para os métodos de instância) e ClassInQuestion.getClass()
(para os métodos da classe) .
- Método de instância é um método que não possui static
palavra-chave.
- Método de classe é um método que possui static
palavra - chave.
Sem sincronização, não é garantido em que ordem as leituras e gravações acontecem, possivelmente deixando a variável com lixo.
(Por exemplo, uma variável pode acabar com metade dos bits gravados por um thread e metade dos bits gravados por outro thread, deixando a variável em um estado que nenhum dos threads tentou escrever, mas uma bagunça combinada de ambos.)
Não é suficiente concluir uma operação de gravação em um encadeamento antes (hora do relógio da parede) de outro encadeamento, porque o hardware poderia ter armazenado em cache o valor da variável e o encadeamento de leitura veria o valor em cache em vez do que foi gravado em isto.
Portanto, no caso de Java, você deve seguir o Java Memory Model para garantir que os erros de segmentação não ocorram.
Em outras palavras: use sincronização, operações atômicas ou classes que as utilizem sob o capô.
Fontes
http://docs.oracle.com/javase/specs/jls/se8/html/index.html
Java® Language Specification, 13-02-2015
Pense nisso como uma espécie de catraca como você pode encontrar em um campo de futebol. Existem vapores paralelos de pessoas que querem entrar, mas na catraca são "sincronizados". Somente uma pessoa de cada vez pode passar. Todos aqueles que desejam passar o farão, mas podem ter que esperar até que possam passar.
Qual é a palavra-chave sincronizada?
Os encadeamentos se comunicam principalmente compartilhando o acesso aos campos e os campos de referência de objetos a que se referem. Essa forma de comunicação é extremamente eficiente, mas possibilita dois tipos de erros: interferência de encadeamento e erros de consistência de memória . A ferramenta necessária para evitar esses erros é a sincronização.
Os blocos ou métodos sincronizados evitam a interferência do encadeamento e asseguram que os dados sejam consistentes. A qualquer momento, apenas um encadeamento pode acessar um método ou bloco sincronizado ( seção crítica ) adquirindo um bloqueio. Outras threads aguardam a liberação do bloqueio para acessar a seção crítica .
Quando os métodos são sincronizados?
Os métodos são sincronizados quando você adiciona synchronized
à definição ou declaração do método. Você também pode sincronizar um bloco de código específico com um método.
O que significa de forma programática e lógica?
Isso significa que apenas um encadeamento pode acessar a seção crítica adquirindo um bloqueio. A menos que esse thread libere esse bloqueio, todos os outros threads precisarão esperar para adquirir um bloqueio. Eles não têm acesso para entrar na seção crítica sem adquirir a trava.
Isso não pode ser feito com uma mágica. É responsabilidade do programador identificar seções críticas no aplicativo e protegê-lo adequadamente. O Java fornece uma estrutura para proteger seu aplicativo, mas onde e o que todas as seções a serem protegidas são de responsabilidade do programador.
Mais detalhes na página de documentação do java
Bloqueios intrínsecos e sincronização:
A sincronização é criada em torno de uma entidade interna conhecida como bloqueio intrínseco ou bloqueio do monitor. Os bloqueios intrínsecos desempenham um papel nos dois aspectos da sincronização: impor acesso exclusivo ao estado de um objeto e estabelecer relacionamentos anteriores ao passado, essenciais para a visibilidade.
Todo objeto tem um bloqueio intrínseco associado a ele . Por convenção, um encadeamento que precisa de acesso exclusivo e consistente aos campos de um objeto precisa adquirir o bloqueio intrínseco do objeto antes de acessá-los e liberar o bloqueio intrínseco quando terminar com eles.
Diz-se que uma linha possui a trava intrínseca entre o momento em que ela adquiriu a trava e a liberou. Desde que um encadeamento possua um bloqueio intrínseco, nenhum outro encadeamento poderá adquirir o mesmo bloqueio. O outro encadeamento será bloqueado quando tentar obter o bloqueio.
Quando um encadeamento libera um bloqueio intrínseco, é estabelecido um relacionamento de antes do início entre essa ação e qualquer aquisição subsequente do mesmo bloqueio.
A sincronização de métodos tem dois efeitos :
Primeiro, não é possível intercalar duas invocações de métodos sincronizados no mesmo objeto.
Quando um encadeamento está executando um método sincronizado para um objeto, todos os outros encadeamentos que invocam métodos sincronizados para o mesmo bloco de objeto (suspender a execução) até que o primeiro encadeamento seja concluído com o objeto.
Segundo, quando um método sincronizado sai, ele estabelece automaticamente um relacionamento de antes do acontecimento com qualquer chamada subsequente de um método sincronizado para o mesmo objeto.
Isso garante que as alterações no estado do objeto sejam visíveis para todos os threads.
Procure outras alternativas para sincronização em:
Synchronized normal method
equivalente a
Synchronized statement
(use isso)
class A {
public synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(this) {
// all function code
}
}
}
Synchronized static method
equivalente a Synchronized statement
(use class)
class A {
public static synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(A.class) {
// all function code
}
}
}
Instrução sincronizada (usando variável)
class A {
private Object lock1 = new Object();
public void methodA() {
synchronized(lock1 ) {
// all function code
}
}
}
Pois synchronized
, temos ambos Synchronized Methods
e Synchronized Statements
. No entanto, Synchronized Methods
é semelhante ao Synchronized Statements
que precisamos entender Synchronized Statements
.
=> Basicamente, teremos
synchronized(object or class) { // object/class use to provides the intrinsic lock
// code
}
Aqui estão 2 que ajudam a entender synchronized
intrinsic lock
associado a ele.synchronized statement
, adquire automaticamente o intrinsic lock
para esse synchronized statement's
objeto e libera-lo quando o método retorna. Enquanto um segmento possuir um intrinsic lock
, nenhum outro segmento poderá adquirir o SAME lock => thread safe.=> Quando um thread A
invoca synchronized(this){// code 1}
=> todo o código de bloco (classe interna) onde have synchronized(this)
e all synchronized normal method
(classe interna) estão bloqueados porque o mesmo bloqueio. Ele será executado após o thread A
desbloqueio ("// código 1" concluído).
Esse comportamento é semelhante a synchronized(a variable){// code 1}
ou synchronized(class)
.
SAME LOCK => lock (não depende de qual método? Ou de quais instruções?)
Eu prefiro synchronized statements
porque é mais extensível. Por exemplo, no futuro, você só precisa sincronizar uma parte do método. Por exemplo, você tem 2 métodos sincronizados e eles não são relevantes um para o outro; no entanto, quando um thread executa um método, ele bloqueia o outro método (pode ser impedido pelo uso synchronized(a variable)
).
No entanto, aplicar o método sincronizado é simples e o código parece simples. Para algumas classes, existe apenas um método sincronizado, ou todos os métodos sincronizados na classe, relevantes entre si => podemos usar synchronized method
para tornar o código mais curto e fácil de entender
(isso não é relevante para muitos synchronized
, é a diferença entre objeto e classe ou não estático e estático).
synchronized
o método normal ou synchronized(this)
ousynchronized(non-static variable)
ele será sincronizado com base em cada instância do objeto. synchronized
um método estático ou synchronized(class)
ou synchronized(static variable)
ele será sincronizado com base na classehttps://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
Espero que ajude
Aqui está uma explicação de The Java Tutorials .
Considere o seguinte código:
public class SynchronizedCounter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } }
if
count
é uma instância deSynchronizedCounter
, a sincronização desses métodos tem dois efeitos:
- Primeiro, não é possível intercalar duas invocações de métodos sincronizados no mesmo objeto. Quando um encadeamento está executando um método sincronizado para um objeto, todos os outros encadeamentos que invocam métodos sincronizados para o mesmo bloco de objetos (suspender a execução) até que o primeiro encadeamento seja concluído com o objeto.
- Segundo, quando um método sincronizado sai, ele estabelece automaticamente um relacionamento de antes do acontecimento com qualquer chamada subsequente de um método sincronizado para o mesmo objeto. Isso garante que as alterações no estado do objeto sejam visíveis para todos os threads.
Para meu entendimento, sincronizado basicamente significa que o compilador escreve um monitor.enter e monitor.exit em torno do seu método. Como tal, pode ser seguro para threads, dependendo de como é usado (o que quero dizer é que você pode escrever um objeto com métodos sincronizados que não são seguros para threads, dependendo do que sua classe faz).
O que as outras respostas estão faltando é um aspecto importante: barreiras à memória . A sincronização de threads consiste basicamente em duas partes: serialização e visibilidade. Aconselho a todos que pesquisem no Google por "barreira da memória jvm", pois é um tópico não trivial e extremamente importante (se você modificar dados compartilhados acessados por vários threads). Feito isso, aconselho a olhar para as classes do pacote java.util.concurrent que ajudam a evitar o uso da sincronização explícita, o que, por sua vez, ajuda a manter os programas simples e eficientes, talvez até a impedir conflitos.
Um exemplo é ConcurrentLinkedDeque . Juntamente com o padrão de comando, ele permite criar encadeamentos de trabalho altamente eficientes, inserindo os comandos na fila simultânea - sem necessidade de sincronização explícita, sem bloqueios possíveis, sem interrupção explícita (), basta pesquisar a fila chamando take ().
Resumindo: a "sincronização de memória" ocorre implicitamente quando você inicia um thread, um thread termina, você lê uma variável volátil, desbloqueia um monitor (deixa um bloco / função sincronizada) etc. Essa "sincronização" afeta (em certo sentido) as descargas ") todas as gravações feitas antes dessa ação específica. No caso do ConcurrentLinkedDeque mencionado acima , a documentação "diz":
Efeitos de consistência de memória: como em outras coleções simultâneas, as ações em um encadeamento anterior à colocação de um objeto em um ConcurrentLinkedDeque acontecem antes das ações subseqüentes ao acesso ou remoção desse elemento do ConcurrentLinkedDeque em outro encadeamento.
Esse comportamento implícito é um aspecto um tanto pernicioso, porque a maioria dos programadores Java sem muita experiência precisará de muito do que foi dado por causa disso. E, de repente, tropeça nesse encadeamento depois que o Java não está fazendo o que "deveria" fazer na produção, onde há uma carga de trabalho diferente - e é muito difícil testar problemas de simultaneidade.
Sincronizado significa simplesmente que vários encadeamentos, se associados a um único objeto, podem impedir a leitura e gravação sujas se o bloco sincronizado for usado em um objeto específico. Para dar mais clareza, vamos dar um exemplo:
class MyRunnable implements Runnable {
int var = 10;
@Override
public void run() {
call();
}
public void call() {
synchronized (this) {
for (int i = 0; i < 4; i++) {
var++;
System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
}
}
}
}
public class MutlipleThreadsRunnable {
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
Thread t1 = new Thread(runnable1);
t1.setName("Thread -1");
Thread t2 = new Thread(runnable2);
t2.setName("Thread -2");
Thread t3 = new Thread(runnable1);
t3.setName("Thread -3");
t1.start();
t2.start();
t3.start();
}
}
Criamos dois objetos de classe MyRunnable, sendo runnable1 compartilhado com o thread 1 e thread 3 e runnable2 sendo compartilhados apenas com o thread 2. Agora, quando t1 e t3 são iniciados sem o uso sincronizado, a saída PFB sugere que os dois segmentos 1 e 3 afetam simultaneamente o valor var, onde no segmento 2, var possui sua própria memória.
Without Synchronized keyword
Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -2 var value 12
Current Thread Thread -2 var value 13
Current Thread Thread -2 var value 14
Current Thread Thread -1 var value 12
Current Thread Thread -3 var value 13
Current Thread Thread -3 var value 15
Current Thread Thread -1 var value 14
Current Thread Thread -1 var value 17
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 18
Usando o Synchronzied, o thread 3 aguarda a conclusão do thread 1 em todos os cenários. Existem dois bloqueios adquiridos, um no runnable1 compartilhado pelo thread 1 e no thread 3 e outro no runnable2 compartilhado apenas pelo thread 2.
Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18
Sincronizado simples significa que não há dois segmentos que possam acessar o bloco / método simultaneamente. Quando dizemos que qualquer bloco / método de uma classe está sincronizado, significa que apenas um thread pode acessá-los por vez. Internamente, o encadeamento que tenta acessá-lo primeiro bloqueia esse objeto e, enquanto esse bloqueio não estiver disponível, nenhum outro encadeamento poderá acessar qualquer um dos métodos / blocos sincronizados dessa instância da classe.
Observe que outro encadeamento pode acessar um método do mesmo objeto que não está definido para ser sincronizado. Um encadeamento pode liberar o bloqueio chamando
Object.wait()
synchronized
O bloco em Java é um monitor em multithreading. synchronized
bloco com o mesmo objeto / classe pode ser executado por apenas um thread, todos os outros estão aguardando. Pode ajudar na race condition
situação em que vários threads tentam atualizar a mesma variável (a primeira etapa é usar volatile
About )
Java 5
estendido synchronized
apoiando happens-before
[Sobre]
Um desbloqueio (saída sincronizada de bloco ou método) de um monitor ocorre antes de cada bloqueio subsequente (entrada sincronizada de bloco ou método) desse mesmo monitor.
O próximo passo é java.util.concurrent