Qual é a diferença entre os métodos lazySet
e set
de AtomicInteger
? A documentação não tem muito a dizer sobre lazySet
:
Eventualmente define o valor fornecido.
Parece que o valor armazenado não será definido imediatamente para o valor desejado, mas será programado para ser definido em algum momento no futuro. Mas, qual é o uso prático desse método? Algum exemplo?
fonte
Atomic*
escopo).lazySet pode ser usado para comunicação entre threads rmw, porque xchg é atômico, quanto à visibilidade, quando o processo de thread do gravador modifica a localização de uma linha de cache, o processador da thread do leitor o verá na próxima leitura, porque o protocolo de coerência do cache da CPU Intel garantirá LazySet funciona, mas a linha do cache será atualizada na próxima leitura, novamente, a CPU precisa ser moderna o suficiente.
http://sc.tamu.edu/systems/eos/nehalem.pdf Para Nehalem, que é uma plataforma de multiprocessador, os processadores têm a capacidade de "espionar" (espionar) o barramento de endereço para acessos de outros processadores à memória do sistema e para seus caches internos. Eles usam essa capacidade de espionagem para manter seus caches internos consistentes com a memória do sistema e com os caches em outros processadores interconectados. Se, por meio de espionagem, um processador detectar que outro processador pretende gravar em um local da memória que atualmente armazenou em cache no estado compartilhado, o processador de espionagem invalidará seu bloco de cache, forçando-o a realizar um preenchimento de linha de cache na próxima vez que acessar o mesmo local de memória .
oracle hotspot jdk para arquitetura de cpu x86->
lazySet == unsafe.putOrderedLong == xchg rw (instrução asm que serve como uma barreira suave que custa 20 ciclos na CPU Intel Nehelem)
em x86 (x86_64), tal barreira é muito mais barata em termos de desempenho do que volátil ou AtomicLong getAndAdd,
Em um produtor, um cenário de fila de consumidor, a barreira suave xchg pode forçar a linha de códigos antes do lazySet (sequência + 1) para que o encadeamento do produtor aconteça ANTES de qualquer código de encadeamento do consumidor que consumirá (trabalhará) os novos dados, é claro o thread do consumidor precisará verificar atomicamente se a sequência do produtor foi incrementada por exatamente um usando um compareAndSet (sequência, sequência + 1).
Rastreei o código-fonte do Hotspot para encontrar o mapeamento exato do lazySet para o código cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong -> definição SET_FIELD_VOLATILE -> OrderAccess: release_store_fence. Para x86_64, OrderAccess: release_store_fence é definido como usando a instrução xchg.
Você pode ver como ele está exatamente definido no jdk7 (doug lea está trabalhando em algumas coisas novas para o JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp
você também pode usar o hdis para desmontar o assembly do código lazySet em ação.
Há outra questão relacionada: precisamos de mfence ao usar xchg
fonte
Uma discussão mais ampla sobre as origens e a utilidade de lazySet e putOrdered subjacente pode ser encontrada aqui: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html
Para resumir: lazySet é uma gravação fraca e volátil, no sentido de que atua como um armazenamento de armazenamento e não um limite de carregamento de armazenamento. Isso se resume a lazySet sendo JIT compilado para uma instrução MOV que não pode ser reordenada pelo compilador, em vez da instrução significativamente mais cara usada para um conjunto volátil.
Ao ler o valor você sempre acaba fazendo uma leitura volátil (com um Atomic * .get () em qualquer caso).
O lazySet oferece a um único gravador um mecanismo de gravação volátil consistente, ou seja, é perfeitamente legítimo para um único gravador usar o lazySet para incrementar um contador, vários threads incrementando o mesmo contador terão que resolver as gravações concorrentes usando CAS, que é exatamente o que acontece em as capas do Atomic * para incAndGet.
fonte
StoreStore
barreira simples , mas não umaStoreLoad
?Do resumo do pacote atômico simultâneo
lazySet tem os efeitos de memória de escrever (atribuir) uma variável volátil, exceto que permite reordenamentos com ações de memória subsequentes (mas não anteriores) que não impõem restrições de reordenamento com gravações não voláteis comuns. Entre outros contextos de uso, lazySet pode ser aplicado ao anular, para fins de coleta de lixo, uma referência que nunca é acessada novamente.
Se você está curioso sobre o lazySet, você também deve a si mesmo outras explicações
fonte
Aqui está o meu entendimento, corrija-me se eu estiver errado: Você pode pensar em
lazySet()
como "semi" volátil: é basicamente uma variável não volátil em termos de leitura por outros tópicos, ou seja, o valor definido por lazySet pode não ser visível para outros tópicos. Mas ele se torna volátil quando ocorre outra operação de gravação (pode ser de outros threads). O único impacto do lazySet que posso imaginar écompareAndSet
. Portanto, se você usarlazySet()
,get()
de outros threads ainda pode obter o valor antigo, mascompareAndSet()
sempre terá o novo valor, pois é uma operação de gravação.fonte
compareAndSet
?Re: tentativa de emburrecer -
Você pode pensar nisso como uma forma de tratar um campo volátil como se não fosse volátil para uma operação de armazenamento em particular (por exemplo: ref = null;).
Isso não é perfeitamente preciso, mas deve ser o suficiente para que você possa tomar uma decisão entre "OK, eu realmente não me importo" e "Hmm, deixe-me pensar um pouco sobre isso".
fonte