Suponha que eu tenha um campo acessado simultaneamente e que seja lido muitas vezes e raramente gravado.
public Object myRef = new Object();
Digamos que um Thread T1 esteja configurando myRef para outro valor, uma vez por minuto, enquanto N outros Threads lerão myRef bilhões de vezes contínua e simultaneamente. Eu só preciso que myRef seja eventualmente visível para todos os threads.
Uma solução simples seria usar um AtomicReference ou simplesmente volátil como este:
public volatile Object myRef = new Object();
No entanto, leituras voláteis após um incorrer em um custo de desempenho. Eu sei que é minúsculo, é mais algo que eu imagino do que algo que eu realmente preciso. Portanto, não vamos nos preocupar com desempenho e suponha que isso seja uma questão puramente teórica.
Portanto, a pergunta se resume a: Existe uma maneira de ignorar com segurança leituras voláteis de referências que raramente são gravadas, fazendo algo no site de gravação?
Após algumas leituras, parece que as barreiras de memória podem ser o que eu preciso. Portanto, se um construto como esse existisse, meu problema seria resolvido:
- Escreva
- Invocar barreira (sincronização)
- Tudo é sincronizado e todos os threads verão o novo valor. (sem um custo permanente nos sites de leitura, pode ser obsoleto ou incorrer em um custo único à medida que os caches são sincronizados, mas depois disso tudo volta ao campo normal até a próxima gravação).
Existe tal construção em Java ou em geral? Neste ponto, não posso deixar de pensar que, se algo assim existisse, ele já teria sido incorporado aos pacotes atômicos pelas pessoas mais inteligentes que os mantinham. (A leitura e a gravação desproporcionalmente frequentes podem não ter sido um caso para cuidar?) Então, talvez haja algo errado em meu pensamento e essa construção não seja possível?
Eu já vi alguns exemplos de código usar 'volátil' para uma finalidade semelhante, explorando o que acontece antes do contrato. Há um campo de sincronização separado, por exemplo:
public Object myRef = new Object();
public volatile int sync = 0;
e na escrita do tópico / site:
myRef = new Object();
sync += 1 //volatile write to emulate barrier
Não sei se isso funciona, e alguns argumentam que isso funciona apenas na arquitetura x86. Depois de ler as seções relacionadas no JMS, acho que é garantido que funcione apenas se essa gravação volátil estiver associada a uma leitura volátil dos threads que precisam ver o novo valor de myRef. (Portanto, não se livre da leitura volátil).
Voltando à minha pergunta original; Isso é possível em tudo? É possível em Java? É possível em uma das novas APIs no Java 9 VarHandles?
sync += 1;
e o encadeamento do leitor lerem o sync
valor, eles também verão a myRef
atualização. Como você só precisa que os leitores vejam a atualização eventualmente , poderá usar isso como vantagem para ler a sincronização apenas a cada milésima iteração do encadeamento do leitor, ou algo semelhante. Mas você pode fazer um truque semelhante com volatile
, demasiado - apenas cache o myRef
campo nos leitores para 1000 iterações, em seguida, lê-lo novamente usando volátil ...
sync
campo, não, os leitores não tocariam no sync
campo em todas as iterações, eles fariam isso oportunisticamente, quando desejassem verificar se houve uma atualização. Dito isto, uma solução mais simples seria para armazenar em cache o myRef
para um 1000 rounds, então relê-lo ...