Existe uma vantagem em usar um Método Sincronizado em vez de um Bloco Sincronizado?

Alguém pode me dizer a vantagem do método sincronizado sobre o bloco sincronizado com um exemplo?

Alguém pode me dizer a vantagem do método sincronizado sobre o bloco sincronizado com um exemplo? Obrigado.

Não há uma vantagem clara de usar o método sincronizado sobre o bloco.

Talvez o único (mas eu não chamaria isso de vantagem) é que você não precisa include a referência de object.

Método:

 public synchronized void method() { // blocks "this" from here.... ... ... ... } // to here 

Quadra:

 public void method() { synchronized( this ) { // blocks "this" from here .... .... .... .... } // to here... } 

Vejo? Nenhuma vantagem em tudo.

Os blocos têm vantagens sobre os methods, principalmente na flexibilidade, porque você pode usar outro object como bloqueio enquanto a synchronization do método bloquearia todo o object.

Comparar:

 // locks the whole object ... private synchronized void someInputRelatedWork() { ... } private synchronized void someOutputRelatedWork() { ... } 

vs.

 // Using specific locks Object inputLock = new Object(); Object outputLock = new Object(); private void someInputRelatedWork() { synchronized(inputLock) { ... } } private void someOutputRelatedWork() { synchronized(outputLock) { ... } } 

Além disso, se o método crescer, você ainda poderá manter a seção sincronizada separada:

  private void method() { ... code here ... code here ... code here synchronized( lock ) { ... very few lines of code here } ... code here ... code here ... code here ... code here } 

A única diferença real é que um bloco sincronizado pode escolher em qual object ele sincroniza. Um método sincronizado só pode usar 'this' (ou a instância de class correspondente para um método de class sincronizado). Por exemplo, estes são semanticamente equivalentes:

 synchronized void foo() { ... } void foo() { synchronized (this) { ... } } 

O último é mais flexível, pois pode competir pelo bloqueio associado de qualquer object, geralmente uma variável de membro. Também é mais granular, porque você poderia executar código concorrente antes e depois do bloco, mas ainda dentro do método. Naturalmente, você poderia facilmente usar um método sincronizado refatorando o código simultâneo em methods não sincronizados separados. Use o que tornar o código mais compreensível.

Método Sincronizado

Prós:

  • Seu IDE pode indicar os methods sincronizados.
  • A syntax é mais compacta.
  • Força a dividir os blocos sincronizados para separar methods.

Contras:

  • Sincroniza com isso e permite que pessoas de fora também sincronizem com ele.
  • É mais difícil mover o código fora do bloco sincronizado.

Bloco sincronizado

Prós:

  • Permite usar uma variável privada para o bloqueio e forçar o bloqueio a permanecer dentro da class.
  • Blocos sincronizados podem ser encontrados procurando referências à variável.

Contras:

  • A syntax é mais complicada e, portanto, dificulta a leitura do código.

Pessoalmente, prefiro usar methods sincronizados com classs voltadas apenas para a coisa que precisa de synchronization. Essa class deve ser o menor possível e, portanto, deve ser fácil revisar a synchronization. Outros não precisam se preocupar com a synchronization.

A principal diferença é que, se você usar um bloco sincronizado, poderá bloquear um object diferente do que permite ser muito mais flexível.

Suponha que você tenha uma fila de mensagens e vários produtores e consumidores de mensagens. Nós não queremos que os produtores interfiram uns com os outros, mas os consumidores devem ser capazes de recuperar mensagens sem ter que esperar pelos produtores. Então nós apenas criamos um object

 Object writeLock = new Object(); 

E a partir de agora toda vez que um produtor quiser adicionar uma nova mensagem, nós apenas bloquearemos isso:

 synchronized(writeLock){ // do something } 

Assim, os consumidores ainda podem ler e os produtores serão bloqueados.

Método sincronizado

Métodos sincronizados têm dois efeitos.
Primeiro, quando um thread está executando um método sincronizado para um object, todos os outros threads que invocam methods sincronizados para o mesmo bloco de object (suspende a execução) até que o primeiro thread seja feito com o object.

Segundo, quando um método sincronizado é encerrado, ele estabelece automaticamente um relacionamento acontece antes de qualquer chamada subseqüente de um método sincronizado para o mesmo object. Isso garante que as alterações no estado do object sejam visíveis para todos os threads.

Observe que os construtores não podem ser sincronizados – usar a palavra-chave sincronizada com um construtor é um erro de syntax. Os construtores de synchronization não fazem sentido, porque somente o segmento que cria um object deve ter access a ele enquanto está sendo construído.

Declaração sincronizada

Ao contrário dos methods sincronizados, as instruções sincronizadas devem especificar o object que fornece o bloqueio intrínseco: Na maioria das vezes, uso isso para sincronizar o access a uma lista ou mapa, mas não desejo bloquear o access a todos os methods do object.

Q: Bloqueios Intrínsecos e Sincronização A synchronization é construída em torno de uma entidade interna conhecida como bloqueio intrínseco ou bloqueio de monitor. (A especificação da API geralmente se refere a essa entidade simplesmente como um “monitor”.) Os bloqueios intrínsecos desempenham um papel em ambos os aspectos da synchronization: impor access exclusivo ao estado de um object e estabelecer relacionamentos antes de relacionamentos essenciais à visibilidade.

Todo object possui um bloqueio intrínseco associado a ele. Por convenção, um encadeamento que precisa de access exclusivo e consistente aos campos de um object precisa adquirir o bloqueio intrínseco do object antes de acessá-los e depois liberar o bloqueio intrínseco quando é feito com eles. Diz-se que um encadeamento possui a fechadura intrínseca entre o momento em que adquiriu a fechadura e liberou a fechadura. Contanto que um thread possua um bloqueio intrínseco, nenhum outro thread pode adquirir o mesmo bloqueio. O outro segmento será bloqueado quando tentar adquirir o bloqueio.

 package test; public class SynchTest implements Runnable { private int c = 0; public static void main(String[] args) { new SynchTest().test(); } public void test() { // Create the object with the run() method Runnable runnable = new SynchTest(); Runnable runnable2 = new SynchTest(); // Create the thread supplying it with the runnable object Thread thread = new Thread(runnable,"thread-1"); Thread thread2 = new Thread(runnable,"thread-2"); // Here the key point is passing same object, if you pass runnable2 for thread2, // then its not applicable for synchronization test and that wont give expected // output Synchronization method means "it is not possible for two invocations // of synchronized methods on the same object to interleave" // Start the thread thread.start(); thread2.start(); } public synchronized void increment() { System.out.println("Begin thread " + Thread.currentThread().getName()); System.out.println(this.hashCode() + "Value of C = " + c); // If we uncomment this for synchronized block, then the result would be different // synchronized(this) { for (int i = 0; i < 9999999; i++) { c += i; } // } System.out.println("End thread " + Thread.currentThread().getName()); } // public synchronized void decrement() { // System.out.println("Decrement " + Thread.currentThread().getName()); // } public int value() { return c; } @Override public void run() { this.increment(); } } 

Cruzar diferentes saídas com o método sincronizado, bloquear e sem synchronization.

Nota: os methods e blocos sincronizados estáticos funcionam no object Class.

 public class MyClass { // locks MyClass.class public static synchronized void foo() { // do something } // similar public static void foo() { synchronized(MyClass.class) { // do something } } } 

Quando o compilador java converte seu código-fonte em código de byte, ele lida com methods sincronizados e blocos sincronizados de maneira muito diferente.

Quando a JVM executa um método sincronizado, o encadeamento de execução identifica que a estrutura method_info do método tem o sinalizador ACC_SYNCHRONIZED definido, em seguida, ele adquire automaticamente o bloqueio do object, chama o método e libera o bloqueio. Se ocorrer uma exceção, o encadeamento libera automaticamente o bloqueio.

Sincronizar um bloco de método, por outro lado, ignora o suporte embutido da JVM para obter o bloqueio de um object e o tratamento de exceção e requer que a funcionalidade seja explicitamente gravada em código de byte. Se você ler o código de byte de um método com um bloco sincronizado, verá mais de uma dúzia de operações adicionais para gerenciar essa funcionalidade.

Isso mostra as chamadas para gerar um método sincronizado e um bloco sincronizado:

 public class SynchronizationExample { private int i; public synchronized int synchronizedMethodGet() { return i; } public int synchronizedBlockGet() { synchronized( this ) { return i; } } } 

O método synchronizedMethodGet() gera o seguinte código de byte:

 0: aload_0 1: getfield 2: nop 3: iconst_m1 4: ireturn 

E aqui está o código de bytes do método synchronizedBlockGet() :

 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: aload_0 5: getfield 6: nop 7: iconst_m1 8: aload_1 9: monitorexit 10: ireturn 11: astore_2 12: aload_1 13: monitorexit 14: aload_2 15: athrow 

Uma diferença significativa entre o método sincronizado e o bloco é que, o bloco Sincronizado geralmente reduz o escopo do bloqueio. Como o escopo do bloqueio é inversamente proporcional ao desempenho, é sempre melhor bloquear somente a seção crítica do código. Um dos melhores exemplos de usar o bloco sincronizado é o bloqueio duplo verificado no padrão Singleton, em que, em vez de bloquear todo o método getInstance() , bloqueamos apenas a seção crítica do código que é usada para criar a instância do Singleton. Isso melhora drasticamente o desempenho porque o bloqueio é necessário apenas uma ou duas vezes.

Ao usar methods sincronizados, você precisará ter cuidado extra se você misturar ambos os methods sincronizados estáticos e não estáticos sincronizados.

Na maioria das vezes eu uso isso para sincronizar o access a uma lista ou mapa, mas não quero bloquear o access a todos os methods do object.

No código a seguir, um thread modificando a lista não bloqueará a espera por um thread que esteja modificando o mapa. Se os methods fossem sincronizados no object, cada método teria que esperar, mesmo que as modificações que eles estão fazendo não entrassem em conflito.

 private List myList = new ArrayList(); private Map(); public void put( String s, Bar b ) { synchronized( myMap ) { myMap.put( s,b ); // then some thing that may take a while like a database access or RPC or notifying listeners } } public void hasKey( String s, ) { synchronized( myMap ) { myMap.hasKey( s ); } } public void add( Foo f ) { synchronized( myList ) { myList.add( f ); // then some thing that may take a while like a database access or RPC or notifying listeners } } public Thing getMedianFoo() { Foo med = null; synchronized( myList ) { Collections.sort(myList); med = myList.get(myList.size()/2); } return med; } 

Com blocos sincronizados, você pode ter vários sincronizadores, para que várias coisas simultâneas, mas sem conflito, possam ocorrer ao mesmo tempo.

Os methods sincronizados podem ser verificados usando a API de reflection. Isso pode ser útil para testar alguns contratos, como todos os methods no modelo são sincronizados .

O snippet a seguir imprime todos os methods sincronizados do Hashtable:

 for (Method m : Hashtable.class.getMethods()) { if (Modifier.isSynchronized(m.getModifiers())) { System.out.println(m); } } 

Nota importante sobre o uso do bloco sincronizado: cuidado com o que você usa como object de bloqueio!

O trecho de código de user2277816 acima ilustra esse ponto em que uma referência a um literal de string é usada como object de bloqueio. Perceba que literais string são automaticamente internados em Java e você deve começar a ver o problema: cada pedaço de código que sincroniza no literal “lock”, compartilha o mesmo bloqueio! Isso pode facilmente levar a deadlocks com partes de código completamente não relacionadas.

Não são apenas objects String que você precisa ter cuidado. As primitivas in a box também são um perigo, já que os methods autoboxing e valueOf podem reutilizar os mesmos objects, dependendo do valor.

Para obter mais informações, consulte: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused

Muitas vezes, usar um bloqueio em um nível de método é muito rude. Por que bloquear um trecho de código que não acessa nenhum recurso compartilhado bloqueando um método inteiro. Como cada object tem um bloqueio, você pode criar objects fictícios para implementar a synchronization em nível de bloco. O nível de bloco é mais eficiente porque não bloqueia todo o método.

Aqui algum exemplo

Nível do Método

 class MethodLevel { //shared among threads SharedResource x, y ; public void synchronized method1() { //multiple threads can't access } public void synchronized method2() { //multiple threads can't access } public void method3() { //not synchronized //multiple threads can access } } 

Nível de bloco

 class BlockLevel { //shared among threads SharedResource x, y ; //dummy objects for locking Object xLock = new Object(); Object yLock = new Object(); public void method1() { synchronized(xLock){ //access x here. thread safe } //do something here but don't use SharedResource x, y // because will not be thread-safe synchronized(xLock) { synchronized(yLock) { //access x,y here. thread safe } } //do something here but don't use SharedResource x, y //because will not be thread-safe }//end of method1 } 

[Editar]

Para Collection como Vector e Hashtable elas são sincronizadas quando ArrayList ou HashMap não estão e você precisa definir a palavra-chave sincronizada ou invocar o método sincronizado Collections:

 Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map List myList = Collections.synchronizedList (myList); // single lock for the entire list 

O método sincronizado é usado para bloquear todos os objects O bloco sincronizado é usado para bloquear objects específicos

A única diferença: os blocos sincronizados permitem o bloqueio granular, ao contrário do método sincronizado

Basicamente, blocos synchronized ou methods foram usados ​​para escrever código seguro de thread evitando erros de inconsistência de memory.

Esta questão é muito antiga e muitas coisas foram alteradas durante os últimos 7 anos. Novas construções de programação foram introduzidas para segurança de thread.

Você pode obter segurança de thread usando a API de simultaneidade avançada em vez de blocos synchronied . Esta página de documentação fornece boas construções de programação para obter segurança de thread.

Os Objetos de Bloqueio suportam idiomas de bloqueio que simplificam muitos aplicativos simultâneos.

Executores definem uma API de alto nível para lançar e gerenciar encadeamentos. As implementações do executor fornecidas pelo java.util.concurrent fornecem gerenciamento de conjunto de encadeamentos adequado para aplicativos de grande escala.

Coleções simultâneas facilitam o gerenciamento de grandes collections de dados e podem reduzir muito a necessidade de synchronization.

As Variáveis ​​Atômicas possuem resources que minimizam a synchronization e ajudam a evitar erros de consistência de memory.

ThreadLocalRandom (no JDK 7) fornece geração eficiente de números pseudo-randoms de vários segmentos.

Melhor substituto para synchronization é o ReentrantLock , que usa a API Lock

Uma exclusão mútua reentrante Bloqueie com o mesmo comportamento básico e semântica que o bloqueio de monitor implícito acessado usando methods e instruções sincronizados, mas com resources estendidos.

Exemplo com bloqueios:

 class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } } 

Consulte os pacotes java.util.concurrent e java.util.concurrent.atomic também para outras construções de programação.

Consulte também esta questão relacionada:

Sincronização vs Bloqueio

Em geral, estes são basicamente os mesmos, além de serem explícitos sobre o monitor do object que está sendo usado versus o implícito deste object. Uma desvantagem dos methods sincronizados que, às vezes, desconheço é que, ao usar a referência “this” para sincronizar, você está deixando aberta a possibilidade de objects externos bloquearem o mesmo object. Isso pode ser um bug muito sutil se você se deparar com isso. A synchronization em um Objeto explícito interno ou outro campo existente pode evitar esse problema, encapsulando completamente a synchronization.

Como já foi dito aqui o bloco sincronizado pode usar variável definida pelo usuário como object de bloqueio, quando a function sincronizada usa apenas “isto”. E, claro, você pode manipular com áreas da sua function que devem ser sincronizadas. Mas todo mundo diz que não há diferença entre a function sincronizada e o bloco que cobre a function inteira usando “isso” como object de bloqueio. Isso não é verdade, a diferença está no código de byte que será gerado em ambas as situações. No caso de uso de bloco sincronizado deve ser alocada variável local que contém referência a “isto”. E como resultado, teremos um tamanho um pouco maior para a function (não relevante se você tiver apenas poucas funções).

Explicação mais detalhada da diferença que você pode encontrar aqui: http://www.artima.com/insidejvm/ed2/threadsynchP.html

No caso de methods sincronizados, o bloqueio será adquirido em um object. Mas se você for com o bloco sincronizado, você tem a opção de especificar um object no qual o bloqueio será adquirido.

Exemplo:

  Class Example { String test = "abc"; // lock will be acquired on String test object. synchronized (test) { // do something } lock will be acquired on Example Object public synchronized void testMethod() { // do some thing } } 

Eu sei que esta é uma pergunta antiga, mas com a minha rápida leitura das respostas aqui, eu realmente não vi ninguém mencionar que às vezes um método synchronized pode ser o bloqueio errado .
Do Java Concurrency In Practice (pág. 72):

 public class ListHelper { public List list = Collections.syncrhonizedList(new ArrayList<>()); ... public syncrhonized boolean putIfAbsent(E x) { boolean absent = !list.contains(x); if(absent) { list.add(x); } return absent; } 

O código acima tem a aparência de ser thread-safe. No entanto, na realidade não é. Nesse caso, o bloqueio é obtido na instância da class. No entanto, é possível que a lista seja modificada por outro thread que não esteja usando esse método. A abordagem correta seria usar

 public boolean putIfAbsent(E x) { synchronized(list) { boolean absent = !list.contains(x); if(absent) { list.add(x); } return absent; } } 

O código acima bloquearia todos os threads que tentassem modificar a lista de modificar a lista até que o bloco sincronizado fosse concluído.

Na prática, a vantagem dos methods sincronizados sobre os blocos sincronizados é que eles são mais resistentes a idiotas; porque você não pode escolher um object arbitrário para bloquear, você não pode usar mal a syntax do método sincronizado para fazer coisas estúpidas como travar em uma cadeia literal ou bloquear o conteúdo de um campo mutável que é alterado de debaixo dos segmentos.

Por outro lado, com methods sincronizados você não pode proteger o bloqueio de ser adquirido por qualquer thread que possa obter uma referência ao object.

Então, usar sincronizado como um modificador em methods é melhor para proteger seus vacinadores de se machucarem, enquanto usar blocos sincronizados em conjunto com objects de bloqueio finais privados é melhor para proteger seu próprio código dos invasores de vacas.

De um resumo de especificação de Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

A instrução sincronizada (§14.17) calcula uma referência a um object; Em seguida, ele tenta executar uma ação de bloqueio nesse object e não prossegue até que a ação de bloqueio seja concluída com êxito. …

Um método sincronizado (§8.4.3.5) executa automaticamente uma ação de bloqueio quando é invocado; seu corpo não é executado até que a ação de bloqueio seja concluída com sucesso. Se o método for um método de instância , ele bloqueará o bloqueio associado à instância para a qual ele foi chamado (ou seja, o object que será conhecido durante a execução do corpo do método). Se o método for estático , ele bloqueará o bloqueio associado ao object Class que representa a class na qual o método está definido. …

Com base nessas descrições, eu diria que a maioria das respostas anteriores está correta, e um método sincronizado pode ser particularmente útil para methods estáticos, onde você teria que descobrir como obter o “object Class que representa a class na qual o método foi definiram.”

Edit: Eu originalmente pensei que estas eram citações das especificações reais de Java. Esclarecido que esta página é apenas um resumo / explicação da especificação

TLDR; Nem use o modificador synchronized nem a expressão synchronized(this){...} mas synchronized(myLock){...} onde myLock é um campo de instância final que mantém um object privado.


A diferença entre usar o modificador synchronized na declaração do método e a expressão synchronized(..){ } no corpo do método é esta:

  • O modificador synchronized especificado na assinatura do método
    1. é visível no JavaDoc gerado,
    2. é programaticamente determinável via reflection ao testar o modificador de um método para Modifier.SYNCHRONIZED ,
    3. requer menos digitação e indução em comparação com synchronized(this) { .... } e
    4. (dependendo do seu IDE) é visível no esboço da class e na conclusão do código,
    5. usa o object this como bloqueio quando declarado no método não estático ou na class envolvente quando declarado em um método estático.
  • A expressão synchronized(...){...} permite que você
    1. para sincronizar apenas a execução de partes do corpo de um método,
    2. para ser usado em um construtor ou em um bloco de boot ( estático ),
    3. para escolher o object de bloqueio que controla o access sincronizado.

No entanto, usando o modificador synchronized ou synchronized(...) {...} com this como o object de bloqueio (como no synchronized(this) {...} ), tem a mesma desvantagem. Ambos usam sua própria instância como o object de bloqueio para sincronizar. Isso é perigoso porque não apenas o object em si, mas qualquer outro object / código externo que contenha uma referência a esse object também pode usá-lo como um bloqueio de synchronization com efeitos colaterais potencialmente graves (degradação de desempenho e deadlocks ).

Portanto, a melhor prática é não usar o modificador synchronized nem a expressão synchronized(...) em conjunto com this como object de bloqueio, mas com um object de bloqueio privado para este object. Por exemplo:

 public class MyService { private final lock = new Object(); public void doThis() { synchronized(lock) { // do code that requires synchronous execution } } public void doThat() { synchronized(lock) { // do code that requires synchronous execution } } } 

Você também pode usar vários objects de bloqueio, mas é necessário tomar cuidado especial para garantir que isso não resulte em bloqueios quando usados ​​nesteds.

 public class MyService { private final lock1 = new Object(); private final lock2 = new Object(); public void doThis() { synchronized(lock1) { synchronized(lock2) { // code here is guaranteed not to be executes at the same time // as the synchronized code in doThat() and doMore(). } } public void doThat() { synchronized(lock1) { // code here is guaranteed not to be executes at the same time // as the synchronized code in doThis(). // doMore() may execute concurrently } } public void doMore() { synchronized(lock2) { // code here is guaranteed not to be executes at the same time // as the synchronized code in doThis(). // doThat() may execute concurrently } } } 

Sincronizando com encadeamentos. 1) NUNCA use sincronizado (isso) em um thread que não funciona. Sincronizando com (isso) usa o segmento atual como o object de segmento de bloqueio. Como cada encadeamento é independente de outros encadeamentos, NÃO há coordenação de synchronization. 2) Testes de código mostram que no Java 1.6 em um Mac a synchronization do método não funciona. 3) sincronizado (lockObj) onde lockObj é um object compartilhado comum de todos os threads que sincronizam nele funcionará. 4) ReenterantLock.lock () e .unlock () funcionam. Veja os tutoriais Java para isso.

O código a seguir mostra esses pontos. Ele também contém o vetor thread-safe que seria substituído pelo ArrayList, para mostrar que muitos threads adicionando a um Vector não perdem nenhuma informação, enquanto o mesmo com um ArrayList pode perder informações. 0) O código atual mostra perda de informação devido a condições de corrida A) Comente a linha A rotulada atual, e descomente a linha A acima dela, então execute, o método perde os dados, mas não deve. B) Inverta o passo A, remova o comentário B e // termine o bloco}. Em seguida, execute para ver os resultados sem perda de dados C) Comente B, descomente C. Execute, consulte a synchronization (isto) perde dados, conforme esperado. Não tem tempo para completar todas as variações, espero que isso ajude. Se a synchronization (this) ou a synchronization do método funcionar, indique qual versão do Java e do SO testou. Obrigado.

 import java.util.*; /** RaceCondition - Shows that when multiple threads compete for resources thread one may grab the resource expecting to update a particular area but is removed from the CPU before finishing. Thread one still points to that resource. Then thread two grabs that resource and completes the update. Then thread one gets to complete the update, which over writes thread two's work. DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change 2) Uncomment "synchronized(countLock){ }" - see counts work Synchronized creates a lock on that block of code, no other threads can execute code within a block that another thread has a lock. 3) Comment ArrayList, unComment Vector - See no loss in collection Vectors work like ArrayList, but Vectors are "Thread Safe" May use this code as long as attribution to the author remains intact. /mf */ public class RaceCondition { private ArrayList raceList = new ArrayList(); // simple add(#) // private Vector raceList = new Vector(); // simple add(#) private String countLock="lock"; // Object use for locking the raceCount private int raceCount = 0; // simple add 1 to this counter private int MAX = 10000; // Do this 10,000 times private int NUM_THREADS = 100; // Create 100 threads public static void main(String [] args) { new RaceCondition(); } public RaceCondition() { ArrayList arT = new ArrayList(); // Create thread objects, add them to an array list for( int i=0; i