Pergunta

Sempre que uma questão aparece no SO sobre a sincronização de Java, algumas pessoas estão muito ansiosos para salientar que synchronized(this) deve ser evitado. Em vez disso, eles afirmam, um bloqueio em uma referência particular deve ser preferida.

Algumas das razões apontadas são:

Outras pessoas, inclusive eu, argumentam que synchronized(this) é uma expressão que é muito usada (também em bibliotecas Java), é segura e bem compreendido. Não deve ser evitado porque você tem um bug e você não tem a menor idéia do que está acontecendo em seu programa multithread. Em outras palavras: se é o caso, então usá-lo

.

Estou interessado em ver alguns exemplos do mundo real (nenhum material foobar) onde é crucial evitar um bloqueio em this é preferível quando synchronized(this) também iria fazer o trabalho.

Por isso:? você deve sempre evitar synchronized(this) e substituí-lo com um bloqueio em uma referência particular


Alguns mais info (atualizado conforme respostas são dadas):

  • estamos a falar de sincronização instância
  • tanto na forma implícita (métodos synchronized) e explícito de synchronized(this) são considerados
  • Se você cita Bloch ou outras autoridades sobre o assunto, não deixe de fora as partes que você não gosta (por exemplo, Effective Java, item Segurança de Segmentos: Normalmente é o bloqueio na instância própria, mas há exceções.)
  • se você precisa de granularidade no seu bloqueio diferente synchronized(this) fornece, então synchronized(this) não é tão aplicável que não é a questão
Foi útil?

Solução

Vou cobrir cada ponto separadamente.

  1. Alguns códigos mal pode roubar o seu bloqueio (muito populares esta, também tem um "Acidentalmente" variante)

    Estou mais preocupado com o acidentalmente . O que significa é que este uso de this faz parte de sua classe interface exposta, e deve ser documentado. Às vezes é desejada a capacidade de outro código para usar o seu bloqueio. Isto é verdade para coisas como Collections.synchronizedMap (veja o javadoc).

  2. Todos os métodos sincronizados dentro da mesma classe usar exatamente o mesmo de bloqueio, o que reduz o rendimento

    Este é um pensamento muito simplista; apenas se livrar de synchronized(this) não vai resolver o problema. sincronização adequada para processamento levará mais pensamento.

  3. Está (desnecessariamente) a exposição demasiada informação

    Esta é uma variante do nº 1. Uso de synchronized(this) é parte de sua interface. Se você não quiser / precisar esta exposta, não fazê-lo.

Outras dicas

Bem, em primeiro lugar, deve-se salientar que:

public void blah() {
  synchronized (this) {
    // do stuff
  }
}

é semanticamente equivalente a:

public synchronized void blah() {
  // do stuff
}

que é uma razão para não usar synchronized(this). Você pode argumentar que você pode fazer coisas em torno do bloco synchronized(this). A razão usual é tentar evitar ter de fazer a verificação sincronizada em tudo, o que leva a todos os tipos de problemas de concorrência, especificamente, a dobro verificado bloqueio problema, que só vai para mostrar o quão difícil pode ser a de fazer um threadsafe verificação relativamente simples.

Um bloqueio privado é um mecanismo de defesa, o que nunca é uma má idéia.

Além disso, como você aludiu, fechaduras privadas pode controlar granularidade. Um conjunto de operações em um objeto pode ser totalmente sem relação com o outro, mas synchronized(this) vai excluem o acesso a todos eles.

synchronized(this) apenas realmente não lhe dá nada.

Enquanto você estiver usando sincronizado (this) você estiver usando a instância de classe como a própria fechadura. Isto significa que enquanto o bloqueio é adquirida pela fio 1 , o rosca 2 deve esperar.

Suponha que o seguinte código:

public void method1() {
    // do something ...
    synchronized(this) {
        a ++;      
    }
    // ................
}


public void method2() {
    // do something ...
    synchronized(this) {
        b ++;      
    }
    // ................
}

Método 1 modificando a variável a e método 2 modificando a variável b , a alteração simultânea da mesma variável por dois segmentos deve ser evitado e que é. Mas enquanto thread1 modificar a e thread2 modificar b pode ser realizada sem qualquer condição de corrida.

Infelizmente, o código acima não vai permitir que isso já que estamos usando a mesma referência para uma fechadura; Isto significa que threads mesmo se eles não estão em uma condição de corrida deve esperar e, obviamente, os sacrifícios de código simultaneidade do programa.

A solução é a utilização 2 diferentes fechaduras para :

public class Test {

    private Object lockA = new Object();
    private Object lockB = new Object();

    public void method1() {
        // do something ...
        synchronized(lockA) {
            a ++;      
        }
        // ................
    }


    public void method2() {
        // do something ...
        synchronized(lockB) {
            b ++;      
        }
        // ................
    }

}

O exemplo utiliza acima mais fino granulado fechaduras (2 fechaduras em vez disso um ( LOCKA e lockB para variáveis ?? a e b , respectivamente) e, como resultado permite uma melhor concorrência, por outro lado, tornou-se mais complexo do que o primeiro exemplo ...

Enquanto eu por não concordar aderir cegamente a regras dogmáticas, que o "bloqueio roubar" cenário parece tão excêntrico para você? Um fio poderia realmente adquirir o bloqueio no seu objeto "externamente" (synchronized(theObject) {...}), bloqueando outros segmentos de espera em métodos de instância sincronizados.

Se você não acredita em código malicioso, considere que este código pode vir de terceiros (por exemplo, se você desenvolver algum tipo de servidor de aplicativos).

A versão "acidental" parece menos provável, mas como se costuma dizer, "Faça alguma coisa idiota-prova e alguém vai inventar uma idiota melhor".

Então, eu concordo com a it-depende-on-que-o-class-faz escola de pensamento.


Editar seguinte eljenso é primeira 3 comentários:

Eu nunca experimentou o bloqueio roubar problema, mas aqui é um cenário imaginário:

Vamos dizer que seu sistema é um servlet container, eo objeto que estamos considerando é a implementação ServletContext. Seu método getAttribute deve ser thread-safe, como atributos de contexto são dados compartilhados; assim você declará-lo como synchronized. Imaginemos também que você forneça um serviço público de hospedagem com base na sua implementação recipiente.

Eu sou seu cliente e implantar o meu "bom" servlet em seu site. Acontece que meu código contém uma chamada para getAttribute.

Um hacker, disfarçado de outro cliente, implanta seu servlet malicioso em seu site. Ele contém o seguinte código no método init:

synchronized (this.getServletConfig().getServletContext()) {
   while (true) {}
}

Assumindo que compartilham o mesmo contexto do servlet (permitido pela especificação, enquanto os dois servlets estão na mesma máquina virtual), a minha chamada em getAttribute está bloqueado para sempre. O hacker conseguiu um DoS no meu servlet.

Este ataque não é possível se getAttribute é sincronizado em um bloqueio privado, porque o código 3-parte não pode adquirir esse bloqueio.

Eu admito que o exemplo é planejado e uma visão simplista de como funciona um servlet container, mas IMHO isso prova o ponto.

Assim, gostaria de fazer a minha escolha de design com base em considerações de segurança: terei controle total sobre o código que tenha acesso às instâncias? Qual seria a consequência de um fio de manter um bloqueio em uma instância indefinidamente?

Não parece um consenso diferente no C # e campos de Java nesta A maioria do código Java Eu vi usos:.

// apply mutex to this instance
synchronized(this) {
    // do work here
}

enquanto a maioria dos C opta # código para o sem dúvida mais seguro:

// instance level lock object
private readonly object _syncObj = new object();

...

// apply mutex to private instance level field (a System.Object usually)
lock(_syncObj)
{
    // do work here
}

A C # idioma é certamente mais seguro. Como mencionado anteriormente, sem acesso malicioso / acidental para o bloqueio pode ser feita a partir de fora do exemplo. código Java tem este risco também, , mas parece que a comunidade Java gravitou ao longo do tempo para a versão um pouco menos seguro, mas um pouco mais concisa.

Isso não é concebida como uma escavação contra Java, apenas um reflexo da minha experiência de trabalho em ambos os idiomas.

Depende da situação.
Se houver apenas uma entidade partilha ou mais de um.

Veja o exemplo de trabalho completo aqui

Uma pequena introdução.

Fios e entidades compartilháveis ??
É possível que múltiplas threads para acesso mesma entidade, por exemplo, múltiplos connectionThreads compartilhando um único MessageQueue. Desde os tópicos executados simultaneamente pode haver uma chance de substituir os dados de um por outro que pode ser um confuso situação.
Então, precisamos de alguma maneira de garantir que entidade compartilhável é acessado apenas por uma thread de cada vez. (CONCURRENCY).

bloco sincronizado
bloco sincronizado () é uma maneira de garantir o acesso simultâneo de entidade compartilhável.
Primeiro, uma pequena analogia
Suponha que há duas pessoas P1, P2 (threads) um lavatório (entidade compartilhável) dentro de um banheiro e há uma porta (bloqueio).
Agora queremos uma pessoa para uso lavatório de cada vez.
Uma abordagem é para trancar a porta por P1 quando a porta está trancada P2 espera até p1 completa o seu trabalho
P1 destranca a porta
só então p1 pode usar lavatório.

sintaxe.

synchronized(this)
{
  SHARED_ENTITY.....
}

"isto" desde que o bloqueio intrínseco associado com a classe (desenvolvedor projetada classe Object Java de tal forma que cada objeto pode funcionar como monitor). Acima de abordagem funciona bem quando há apenas uma entidade compartilhada e vários segmentos (1: N).
enter descrição da imagem aqui N compartilháveis ??entidades-M tópicos
Agora pense em uma situação em que há dois lavatório dentro de um banheiro e apenas uma porta. Se estamos usando a abordagem anterior, apenas p1 pode usar um lavatório de cada vez, enquanto p2 vai esperar lá fora. É desperdício de recursos como ninguém está usando B2 (lavatório).
Uma abordagem mais sensato seria a criação de uma pequena sala dentro de banheiro e proporcionar-lhes uma porta por lavatório. Desta forma, o acesso P1 lata B1 e B2 acesso P2 lata e vice-versa.

washbasin1;  
washbasin2;

Object lock1=new Object();
Object lock2=new Object();

  synchronized(lock1)
  {
    washbasin1;
  }

  synchronized(lock2)
  {
    washbasin2;
  }

enter descrição da imagem aqui
enter descrição da imagem aqui

Veja mais em Threads ----> aqui

O pacote java.util.concurrent tem muito reduzido a complexidade do meu código thread-safe. Eu só tenho a evidência anedótica para continuar, mas a maioria dos trabalhos que tenho visto com synchronized(x) parece ser re-implementação de um Lock, Semaphore, ou Trinco, mas usando os monitores de nível inferior.

Com isto em mente, a sincronização usando qualquer um destes mecanismos é análogo a sincronização em um objeto interno, ao invés de vazamento de uma fechadura. Isso é benéfico em que você tem certeza absoluta de que você controlar a entrada no monitor por dois ou mais threads.

  1. Faça o seu imutável dados se é possível (variáveis ??final)
  2. Se você não pode evitar a mutação de dados compartilhados entre vários tópicos, usar construções de programação de alto nível [por exemplo, granular Lock API]

Um bloqueio fornece acesso exclusivo a um recurso compartilhado:. Único segmento em um tempo pode adquirir o bloqueio e todo o acesso ao recurso compartilhado requer que o bloqueio ser adquirido primeira

Código de amostra para uso ReentrantLock que implementa Lock de interface

 class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

Vantagens de bloqueio mais Synchronized (this)

  1. O uso de métodos ou instruções forças sincronizados toda aquisição de bloqueio e liberação de ocorrer de uma forma estruturada em blocos.

  2. implementações de bloqueio proporcionam funcionalidades adicionais através da utilização de métodos e declarações sincronizados proporcionando

    1. Uma tentativa sem bloqueio para adquirir um bloqueio (tryLock())
    2. Uma tentativa de adquirir o bloqueio que pode ser interrompido (lockInterruptibly())
    3. Uma tentativa de adquirir o bloqueio que pode timeout (tryLock(long, TimeUnit)).
  3. A classe Lock também pode fornecer comportamento e semântica que é bastante diferente da do bloqueio implícito monitor, tais como

    1. ordenação garantida
    2. re não utilização participante
    3. Impasse detecção

Tenha um olhar para esta questão SE sobre vários tipos de Locks:

sincronização vs bloqueio

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

Bloquear Objetos fortes idiomas de bloqueio apoio que simplificam muitas aplicações simultâneas.

Executores definir uma API de alto nível para lançar e gerir threads. implementações do executor fornecidos pelo java.util.concurrent fornecer gerenciamento de pool de threads adequado para aplicações em larga escala.

simultâneas coleções torná-lo mais fácil de gerenciar grandes coleções de dados, e pode reduzir muito a necessidade de sincronização.

Variáveis ??Atomic têm características que minimizam erros de consistência de sincronização e ajuda memória evitar.

ThreadLocalRandom (no JDK 7) proporciona a geração eficiente de números pseudo-aleatórios a partir de vários segmentos.

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

Se você decidiu que:

  • a coisa que você precisa fazer é fechamento em o objecto actual; e
  • você quer bloqueá-lo com granularidade menor do que um método todo;

então eu não vejo a um tabu sobre synchronizezd (this).

Algumas pessoas usam deliberadamente sincronizados (this) (em vez de marcar o método sincronizado) dentro de todo o conteúdo de um método, porque eles acham que é "mais clara para o leitor" qual objeto está realmente sendo sincronizados por diante. Enquanto as pessoas estão fazendo uma escolha informada (por exemplo, compreender que ao fazer isso eles estão realmente inserindo bytecodes extras para o método e isso poderia ter um efeito de arrastamento sobre potenciais optimizações), eu particularmente não vejo um problema com este . Você deve sempre documentar o comportamento concorrente de seu programa, então eu não vejo o argumento " 'sincronizado' publica o comportamento" como sendo tão atraente.

Quanto à questão de qual objeto de bloqueio que você deve usar, eu acho que não há nada de errado com a sincronização do objeto atual se este seria esperado pela lógica do que você está fazendo e como sua classe seria tipicamente usado . Por exemplo, com uma coleção, o objeto que seria logicamente esperar bloqueio é geralmente a própria coleção.

Eu acho que há uma boa explicação sobre o porquê de cada uma delas são técnicas vitais em seu cinto em um livro chamado Java simultaneidade na prática, por Brian Goetz. Ele faz um ponto muito claro - você deve usar o mesmo bloqueio "em toda parte" para proteger o estado do seu objeto. método e sincronização sincronizado em um objeto, muitas vezes andam de mãos dadas. Por exemplo. Vector sincroniza todos os seus métodos. Se você tiver um identificador para um objeto vetorial e vão fazer "colocar se ausente", em seguida, apenas Vector sincronizar seus próprios métodos individuais não vai protegê-lo de corrupção do Estado. Você precisa sincronizar usando sincronizado (vectorHandle). Isso resultará na fechadura mesmo ser adquirida por cada segmento que tem um identificador para o vector e protegerá estado geral do vetor. Isso é chamado de cliente de travamento lateral. Nós sabemos como uma questão de fato vector não sincronizado (this) / sincroniza todos os seus métodos e, portanto, a sincronização no objeto vectorHandle irá resultar em uma sincronização apropriada do vector objetos estado. Seu tolo para acreditar que você é o segmento de seguros só porque você está usando uma coleção thread-safe. Esta é precisamente a razão ConcurrentHashMap explicitamente introduzido método putIfAbsent -. Para fazer tais operações atômica

Em resumo

  1. Sincronizando a nível método permite bloqueio do lado do cliente.
  2. Se você tem um objeto de bloqueio privada - faz do lado do cliente bloqueio impossível. Isso é bom se você sabe que sua classe não tem "colocar se ausente" tipo de funcionalidade.
  3. Se você está projetando uma biblioteca -, em seguida, sincronizar sobre este ou sincronizar o método é frequentemente mais sábio. Porque você está raramente em posição de decidir como a sua classe vai ser utilizado.
  4. Vector tinha usado um objeto de bloqueio privado - teria sido impossível de se "colocar se ausente" direito. O código do cliente nunca vai ganhar um identificador para o bloqueio privada quebrando assim a regra fundamental da utilizando o bloqueio exatamente o mesmo para proteger seu estado.
  5. Sincronizar sobre este ou métodos sincronizados têm um problema como outros apontaram - alguém poderia obter um bloqueio e nunca liberá-lo. Todos os outros segmentos iria manter esperando o bloqueio ser liberado.
  6. Assim, sabe o que está fazendo e adotar o que é correto.
  7. Alguém argumentou que ter um objeto de bloqueio privada dá-lhe uma melhor granularidade - por exemplo, se duas operações são independentes - que poderia ser guardado por fechaduras diferentes, resultando em melhor rendimento. Mas isso eu acho que é cheiro de criação e não cheiro de código - se duas operações são completamente alheios porque eles são parte da classe MESMO? Por que uma classe clube funcionalidades não relacionadas em tudo? Pode ser uma classe de utilitário? Hmmmm - alguns util fornecendo a manipulação de cadeia e data do calendário formatação através da mesma instância ?? ... não faz qualquer sentido para mim, pelo menos !!

Não, você não deve sempre . No entanto, eu tendem a evitá-lo quando há várias preocupações sobre um determinado objeto que só precisam ser threadsafe em relação a si mesmos. Por exemplo, você pode ter um objeto de dados mutáveis ??que tem "rótulo" e campos de "pai"; Estes precisam ser threadsafe, mas mudando um precisa não bloquear o outro de ser escrita / leitura. (Na prática, I evitar este declarando os campos volátil e / ou usando wrappers AtomicFoo de java.util.concurrent).

Sincronização em geral é um pouco desajeitado, como ele bate um grande bloqueio para baixo em vez de pensar exatamente como segmentos podem ser autorizados a trabalhar em torno de si. Usando synchronized(this) é ainda mais desajeitado e anti-social, como ele está dizendo "ninguém pode mudar qualquer sobre esta classe, enquanto eu manter o bloqueio". Quantas vezes você realmente precisa fazer isso?

Eu seria muito melhor ter fechaduras mais granulares; mesmo se você quer parar tudo, desde mudar (talvez você está serializadas o objeto), você pode simplesmente adquirir todos os bloqueios para conseguir a mesma coisa, mais ele é mais explícito dessa forma. Quando você usa synchronized(this), não está claro exatamente por que você está na sincronização, ou o que os efeitos colaterais podem ser. Se você usar synchronized(labelMonitor), ou melhor ainda labelLock.getWriteLock().lock(), é claro que você está fazendo e quais os efeitos de sua seção crítica estão limitados a.

Resposta curta :. Você tem que entender a diferença e fazer a escolha de acordo com o código

Long resposta : Em geral eu preferiria tentar evitar sincronizar (este) para reduzir a contenção, mas fechaduras privadas acrescentam complexidade que você tem que estar ciente. Portanto, use a sincronização certa para o trabalho certo. Se você não são tão experientes com multi-threaded programação eu preferiria ficar com bloqueio instância e ler sobre este tema. (Dito isto: apenas usando sincronizar (este) não significa automaticamente que sua classe thread-safe totalmente.) Este é um não um tema fácil, mas uma vez que você se acostumar com isso, a resposta se deve usar < em> sincronizar (este) ou não vem naturalmente.

Um bloqueio é utilizado para qualquer visibilidade ou para proteger alguns dados do concomitante modificação que pode levar a corrida.

Quando você precisa apenas fazer operações de tipo primitivo para ser atômica há opções disponíveis, como AtomicInteger e os gostos.

Mas suponha que você tenha dois números inteiros que estão relacionados uns aos outros como x e y coordenadas, que estão relacionados entre si e devem ser alteradas de uma maneira atômica. Então você teria que protegê-los usando um mesmo bloqueio.

Um bloqueio só deve proteger o estado que está relacionado com o outro. Não menos e não mais. Se você usar synchronized(this) em cada método, em seguida, mesmo se o estado da classe não está relacionado todos os tópicos irá enfrentar contenção mesmo se atualizar estado independente.

class Point{
   private int x;
   private int y;

   public Point(int x, int y){
       this.x = x;
       this.y = y;
   }

   //mutating methods should be guarded by same lock
   public synchronized void changeCoordinates(int x, int y){
       this.x = x;
       this.y = y;
   }
}

No exemplo acima, eu tenho apenas um método que transforma ambos não dois métodos diferentes como x e y x e y e estão relacionados e se eu tinha dado dois métodos diferentes para a mutação x e y separadamente, então não teria sido rosca seguro.

Este exemplo é apenas para demonstrar e não necessariamente a forma como deve ser implementado. A melhor maneira de fazer isso seria a de torná-lo IMMUTABLE .

Agora, em oposição com o exemplo Point, existe um exemplo de TwoCounters já fornecida por @Andreas onde o estado que está protegido por duas fechaduras diferentes, como no estado não está relacionado com o outro.

O processo de utilização de diferentes bloqueios para proteger os estados não relacionados é chamado Bloqueio Striping ou Bloqueio Splitting

A razão para não sincronizar em este é que às vezes você precisa mais de um bloqueio (o segundo bloqueio muitas vezes fica removido após algumas reflexões adicionais, mas você ainda precisa dele no estado intermediário). Se você bloquear em este , você sempre tem que lembrar que um dos dois bloqueios é este ; se você bloquear em um objeto particular, o nome da variável lhe diz isso.

Do ponto de vista do leitor, se você ver o bloqueio em este , você sempre tem que responder às duas perguntas:

  1. que tipo de acesso é protegido por este ?
  2. é um bloqueio realmente suficiente, não alguém introduzir um bug?

Um exemplo:

class BadObject {
    private Something mStuff;
    synchronized setStuff(Something stuff) {
        mStuff = stuff;
    }
    synchronized getStuff(Something stuff) {
        return mStuff;
    }
    private MyListener myListener = new MyListener() {
        public void onMyEvent(...) {
            setStuff(...);
        }
    }
    synchronized void longOperation(MyListener l) {
        ...
        l.onMyEvent(...);
        ...
    }
}

Se dois tópicos começar longOperation() em duas instâncias diferentes de BadObject, eles adquirem seus fechaduras; Quando é hora de invocar l.onMyEvent(...), temos um impasse porque nenhum dos fios pode adquirir bloqueio do outro objeto.

Neste exemplo, pode eliminar o impasse por meio de duas fechaduras, um para operações curtas e um para aqueles longos.

Como já foi dito bloco aqui sincronizado pode usar variável definida pelo usuário como bloqueio de objeto, quando sincronizado usa a função única "isto". E, claro, você pode manipular com áreas de sua função que devem ser sincronizadas e assim por diante.

Mas todo mundo diz que não há diferença entre a função sincronizada e bloco que cobre função inteira usando "isto" como objeto de bloqueio. Isso não é verdade, diferença está em código de bytes que serão gerados em ambas as situações. Em caso de utilização do bloco sincronizado deve ser alocado variável local que detém referência a "isto". E, como resultado, teremos um pouco maior tamanho da função (não é relevante se você tiver apenas poucos número de funções).

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

Também o uso de bloco sincronizado não é boa devido ao ponto de vista que se segue:

A palavra-chave sincronizado é muito limitado em uma área: ao sair de um bloco sincronizado, todos os segmentos que estão aguardando para que o bloqueio deve ser desbloqueado, mas apenas um desses tópicos levará o bloqueio; todos os outros vêem que o bloqueio é tomada e voltar para o estado bloqueado. Isso não é apenas um monte de ciclos de processamento desperdiçados: muitas vezes a mudança de contexto para desbloquear um thread também envolve paginação de memória fora do disco, e isso é muito, muito, caro

.

Para mais detalhes nesta área eu recomendo que você leia este artigo: http://java.dzone.com/articles/synchronized-considered

Isto é realmente apenas suplementar às outras respostas, mas se o seu principal objeção ao uso de objetos particulares para o bloqueio é que ele tumultua sua classe com campos que não estão relacionados com a lógica de negócios, em seguida, o projeto Lombok tem @Synchronized para gerar o clichê em tempo de compilação:

@Synchronized
public int foo() {
    return 0;
}

compila a

private final Object $lock = new Object[0];

public int foo() {
    synchronized($lock) {
        return 0;
    }
}

Um bom exemplo para uso sincronizado (este).

// add listener
public final synchronized void addListener(IListener l) {listeners.add(l);}
// remove listener
public final synchronized void removeListener(IListener l) {listeners.remove(l);}
// routine that raise events
public void run() {
   // some code here...
   Set ls;
   synchronized(this) {
      ls = listeners.clone();
   }
   for (IListener l : ls) { l.processEvent(event); }
   // some code here...
}

Como você pode ver aqui, usamos sincronizar com isso para fácil cooperar de (circular possivelmente infinito de método run) lengthly com alguns métodos sincronizados lá.

Claro que pode muito facilmente ser reescrito com o uso sincronizado no campo privado. Mas às vezes, quando já temos algum projeto com métodos sincronizados (ou seja, classe legado, que derivam, sincronizado (este) pode ser a única solução).

Depende da tarefa que você quer fazer, mas eu não iria utilizá-lo. Além disso, verifique se o fio-save-ness você quiser accompish não poderia ser feito por sincronizar (este), em primeiro lugar? Há também alguns agradável fechaduras na API que podem ajudá-lo:)

Eu só quero mencionar uma possível solução para referências particulares únicos em partes atômicas de código sem dependências. Você pode usar um HashMap estática com fechaduras e um método estático simples chamado atômica () que cria referências necessárias automaticamente usando informações de pilha (nome da classe completo e número de linha). Então você pode usar esse método em declarações Sincronizar sem escrever novo objeto de bloqueio.

// Synchronization objects (locks)
private static HashMap<String, Object> locks = new HashMap<String, Object>();
// Simple method
private static Object atomic() {
    StackTraceElement [] stack = Thread.currentThread().getStackTrace(); // get execution point 
    StackTraceElement exepoint = stack[2];
    // creates unique key from class name and line number using execution point
    String key = String.format("%s#%d", exepoint.getClassName(), exepoint.getLineNumber()); 
    Object lock = locks.get(key); // use old or create new lock
    if (lock == null) {
        lock = new Object();
        locks.put(key, lock);
    }
    return lock; // return reference to lock
}
// Synchronized code
void dosomething1() {
    // start commands
    synchronized (atomic()) {
        // atomic commands 1
        ...
    }
    // other command
}
// Synchronized code
void dosomething2() {
    // start commands
    synchronized (atomic()) {
        // atomic commands 2
        ...
    }
    // other command
}

Evitar usando synchronized(this) como um mecanismo de bloqueio: Isto bloqueia a instância da classe inteira e pode causar bloqueios. Nesses casos, refatorar o código para bloquear apenas um método ou variável específica, dessa forma toda a classe não ficar trancado. Synchronised pode ser usado dentro de nível de método.
Em vez de usar synchronized(this), abaixo mostra o código como você poderia simplesmente bloquear um método.

   public void foo() {
if(operation = null) {
    synchronized(foo) { 
if (operation == null) {
 // enter your code that this method has to handle...
          }
        }
      }
    }

Os meus dois centavos em 2019, embora esta questão poderia ter sido já resolvida.

bloqueio no 'isto' não é ruim se você sabe o que está fazendo, mas por trás do bloqueio cena em 'isto' é (que, infelizmente, o que sincronizado palavra-chave na definição do método permite).

Se você realmente deseja que os usuários de sua classe para ser capaz de 'roubar' o seu bloqueio (ou seja, impedir que outros segmentos de lidar com isso), você realmente quer que todos os métodos sincronizados para esperar enquanto outro método de sincronização está em execução e assim por diante. Ele deve ser intencional e bem pensado fora (e, portanto, documentado para ajudar seus usuários a entender isso).

Para aprofundar, no reverso você deve saber o que você está 'ganhando' (ou 'perder' out on) se você travar em um bloqueio não acessível (ninguém pode 'roubar' o seu bloqueio, você está no controle total e em breve...).

O problema para mim é a palavra-chave sincronizado na assinatura definição de método faz com que seja muito fácil para programadores não pensar sobre o que bloqueio em que é uma coisa importante poderoso para pensar se você don 't quer ter problemas em um programa multi-threaded.

Não se pode argumentar que 'normalmente' você não quer que os usuários de sua classe para ser capaz de fazer essas coisas, ou que 'normalmente' você quer ... Depende do que funcionalidade que você está codificando. Você não pode fazer uma regra de ouro que você não pode prever todos os casos de uso.

Considere, por exemplo, PrintWriter que utiliza um bloqueio interno, mas, em seguida, as pessoas lutam para usá-lo de vários segmentos, se eles não querem a sua saída para intercalam.

Se o seu bloqueio ser acessível fora da classe ou não é a sua decisão como um programador com base no qual funcionalidade a classe tem. É parte da API. Você não pode afastar-se, por exemplo, a partir sincronizado (este) para sincronizado (provateObjet) sem arriscar alterações significativas no código de usá-lo.

Nota 1: Eu sei que você pode conseguir o que quer sincronizados (this) 'alcança' usando um objeto de bloqueio explícito e expô-lo, mas eu acho que é desnecessário se o seu comportamento é bem documentado e você realmente sabe o bloqueio em 'isto' meios.

Nota 2: Eu não concordo com o argumento de que, se algum código é acidentalmente roubar o seu bloqueio é um bug e você tem que resolvê-lo. Isso de certa forma é mesmo argumento dizendo que eu posso fazer todos os meus métodos público, mesmo se eles não são destinadas a ser público. Se alguém é 'acidentalmente' chamando a minha intenção de ser método privado é um bug. Por que permitir que este acidente, em primeiro lugar !!! Se capacidade de roubar o seu bloqueio é um problema para a sua classe não permitem isso. Tão simples como isso.

Eu acho pontos (uma outra pessoa usando seu bloqueio) e dois (todos os métodos que usam o mesmo bloqueio desnecessariamente) pode acontecer em qualquer bastante grande aplicação. Especialmente quando não há nenhuma boa comunicação entre os desenvolvedores.

Não é lançado em pedra, é principalmente uma questão de boas práticas e prevenção de erros.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top