Longo primitivo ou atômico para um contador?
-
20-09-2019 - |
Pergunta
Eu preciso de um balcão de tipo long
Com os seguintes requisitos/fatos:
- Incrementar o contador deve levar o mínimo de tempo possível.
- O contador será escrito apenas por um tópico.
- A leitura do balcão será feita em outro tópico.
- O contador será incrementado regularmente (até alguns milhares de vezes por segundo), mas só será lido uma vez a cada cinco segundos.
- A precisão precisa não é essencial, apenas uma idéia aproximada do tamanho do contador é boa o suficiente.
- O contador nunca é limpo, diminuído.
Com base nesses requisitos, como você escolheria implementar seu contador? Como simples long
, como um volatile long
ou usando um AtomicLong
? Por quê?
No momento eu tenho um volatile long
Mas estava se perguntando se outra abordagem seria melhor. Eu também estou aumentando o meu tempo fazendo ++counter
em oposição a counter++
. Isso é realmente mais eficiente (como fui levado a acreditar em outros lugares) porque não há nenhuma tarefa?
Solução
Dados esses conjuntos de requisitos, eu acho que um volatile
por muito tempo deve ser suficiente. O contador não estaria incorreto com um nãovolatile
Long, mas o leitor pode estar lendo informações obsoletas nesse caso.
Um problema é que lê e escreve para um long
são não requeridos ser atômico, pelo Especificação JVM Se não for declarado volatile
. Isso significaria que o thread de leitura poderia obter um valor praticamente fictício se lê o valor enquanto o tópico de escrita atualizou uma parte do valor, mas não a outra.
A diferença entre ++counter
e counter++
é provavelmente irrelevante, como a JVM perceberá que o valor da expressão não é mais usado e os dois são equivalentes neste caso.
Outras dicas
No Java 8, use Longadder, que é ainda melhor que o atômico, onde a contenção de roscas é alta.
Longadder Javadoc:
Essa classe é geralmente preferível a atômico quando vários threads atualizam uma soma comum usada para fins, como coleta de estatísticas, não para controle de sincronização de granulação fina. Sob baixo contenção de atualização, as duas classes têm características semelhantes. Porém, sob alta contenção, a taxa de transferência esperada desta classe é significativamente maior, às custas de maior consumo de espaço.
Qual é o requisito de tempo de atividade para o seu programa? Você poderia se contentar com uma INT não volátil e leituras racy?
10^4 incrementos / segundo é 1 a cada 100 USEC. A eficiência não é um problema, mas a atomicidade pode ser. Você pode ter 2 cópias e, quando é lido, se não forem iguais, leia novamente.
este artigo fala sobre as possíveis maneiras de implementar um contador, acho que essa implementação deve funcionar para você
class LessNaiveVolatieIdGenerator {
private static volatile long id = 0;
public static long nextId() {
long nextId = (id = id + 1); // or nextId = id++;
return nextId;
}
}