Estou tentando entender o que torna a trava na simultaneidade tão importante se alguém pode usar synchronized (this)
. No código fictício abaixo, eu posso fazer:
- sincronizou o método inteiro ou sincronizou a área vulnerável (
synchronized(this){...}
) - OU bloqueie a área de código vulnerável com um ReentrantLock.
Código:
private final ReentrantLock lock = new ReentrantLock();
private static List<Integer> ints;
public Integer getResult(String name) {
.
.
.
lock.lock();
try {
if (ints.size()==3) {
ints=null;
return -9;
}
for (int x=0; x<ints.size(); x++) {
System.out.println("["+name+"] "+x+"/"+ints.size()+". values >>>>"+ints.get(x));
}
} finally {
lock.unlock();
}
return random;
}
synchronized(this){synchronized(this){//some code}}
, não causará bloqueio. Para bloqueio intrínseco, se eles obtiverem monitor em um recurso e se desejarem novamente, poderão obtê-lo sem um bloqueio morto.Respostas:
Um ReentrantLock não é estruturado , diferentemente das
synchronized
construções - ou seja, você não precisa usar uma estrutura de bloco para bloquear e pode até manter um bloqueio entre os métodos. Um exemplo:É impossível representar esse fluxo através de um único monitor em uma
synchronized
construção.Além disso,
ReentrantLock
suporta pesquisa de bloqueio e espera de bloqueio interrompível que suportam tempo limite .ReentrantLock
também oferece suporte à política de justiça configurável , permitindo um agendamento de encadeamento mais flexível.ReentrantLock
também pode ser mais escalável , com desempenho muito melhor em contenções mais altas. Você pode ler mais sobre isso aqui .Esta alegação foi contestada, no entanto; veja o seguinte comentário:
Quando você deve usar
ReentrantLock
s? De acordo com o artigo do developerWorks ...fonte
ReentrantLockPseudoRandom
código na ligação Lycog está usando a nova marca, fechaduras uncontended cada invocação desetSeed
enext
ReentrantReadWriteLock
é um bloqueio especializado, enquanto quesynchronized(this)
é um bloqueio de uso geral. Eles são semelhantes, mas não são iguais.Você está certo no que poderia usar em
synchronized(this)
vez de,ReentrantReadWriteLock
mas o oposto nem sempre é verdadeiro.Se você quiser entender melhor o que torna
ReentrantReadWriteLock
especial, procure algumas informações sobre a sincronização de encadeamento produtor-consumidor.Em geral, você pode se lembrar de que a sincronização de método inteiro e a sincronização de uso geral (usando a
synchronized
palavra - chave) podem ser usadas na maioria dos aplicativos sem pensar muito na semântica da sincronização, mas se você precisar reduzir o desempenho do seu código, talvez seja necessário explore outros mecanismos de sincronização mais refinados ou para fins especiais.A propósito, o uso
synchronized(this)
- e, em geral, o bloqueio usando uma instância de classe pública - pode ser problemático porque abre seu código para possíveis bloqueios, porque alguém que não sabe intencionalmente pode tentar bloquear seu objeto em outro lugar do programa.fonte
public class MyLock { private final Object protectedLongLockingMonitor = new Object(); private long protectedLong = 0L; public void incrementProtectedLong() { synchronized(protectedLongLockingMonitor) { protectedLong++; } } }
Na página de documentação da oracle sobre ReentrantLock :
Um ReentrantLock pertence ao último bloqueio com êxito, mas ainda não o desbloqueou. Um bloqueio de chamada de thread retornará, adquirindo o bloqueio com êxito, quando o bloqueio não pertencer a outro segmento. O método retornará imediatamente se o segmento atual já possuir o bloqueio.
O construtor para esta classe aceita um parâmetro de justiça opcional . Quando definido como verdadeiro, sob contenção, os bloqueios favorecem a concessão de acesso ao segmento de espera mais longa . Caso contrário, esse bloqueio não garante nenhuma ordem de acesso específica.
Principais recursos do ReentrantLock , de acordo com este artigo
Você pode usar ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock para adquirir ainda mais controle sobre o bloqueio granular nas operações de leitura e gravação.
Veja este artigo de Benjamen sobre o uso de diferentes tipos de ReentrantLocks
fonte
Você pode usar bloqueios de reentrada com uma política de justiça ou tempo limite para evitar a inanição do encadeamento. Você pode aplicar uma política de justiça de encadeamento. ajudará a evitar um encadeamento aguardando para sempre chegar aos seus recursos.
A "política de justiça" escolhe o próximo encadeamento executável a ser executado. É baseado em prioridade, tempo desde a última execução, blá blá
Além disso, o Synchronize pode bloquear indefinidamente se não puder escapar do bloco. O reentrantlock pode ter o tempo limite definido.
fonte
Os bloqueios sincronizados não oferecem nenhum mecanismo de fila de espera no qual, após a execução de um encadeamento, qualquer encadeamento em paralelo possa adquirir o bloqueio. Devido ao qual o encadeamento existente no sistema e em execução por um longo período de tempo nunca tem chance de acessar o recurso compartilhado, levando à inanição.
Os bloqueios reentrantes são muito flexíveis e têm uma política de justiça na qual, se um encadeamento estiver aguardando mais tempo e após a conclusão do encadeamento atualmente em execução, podemos garantir que o encadeamento mais aguardado tenha a chance de acessar o recurso compartilhado, diminuindo o rendimento do sistema e tornando-o mais demorado.
fonte
Vamos supor que este código esteja sendo executado em um thread:
Como o encadeamento possui o bloqueio, permitirá que várias chamadas sejam bloqueadas () e, portanto, reinsira o bloqueio. Isso pode ser conseguido com uma contagem de referência, para que não seja necessário adquirir o bloqueio novamente.
fonte
Uma coisa a ter em mente é:
O nome ' ReentrantLock ' fornece uma mensagem errada sobre outro mecanismo de bloqueio de que eles não são reentrantes. Isso não é verdade. O bloqueio adquirido via 'sincronizado' também é reentrante em Java.
A principal diferença é que 'sincronizado' usa bloqueio intrínseco (um que todo objeto possui) enquanto a API de bloqueio não.
fonte
Eu acho que os métodos wait / notify / notifyAll não pertencem à classe Object, pois polui todos os objetos com métodos que raramente são usados. Eles fazem muito mais sentido em uma classe de bloqueio dedicada. Portanto, desse ponto de vista, talvez seja melhor usar uma ferramenta projetada explicitamente para o trabalho em questão - por exemplo, ReentrantLock.
fonte