O pacote java.util.concurrent.atomic no Java fornece um conjunto de classes utilitárias que permitem a programação thread-safe sem o uso de bloqueios (lock-free) em variáveis individuais. Essas classes expandem o conceito de variáveis, campos e elementos de array volatile, adicionando operações de atualização condicional atômica (como Compare-And-Swap - CAS).
- Classes para Atualização Atômica de Valores Escalares e Arrays
AtomicBoolean
Diferente do que o nome sugere, o AtomicBoolean não utiliza um tipo booleano nativo internamente. Ele armazena o valor como um inteiro (int), onde 0 representa false e 1 representa true. A manipulação de memória é feita através da classe Unsafe, que calcula o deslocamento (offset) do campo para realizar operações CAS.
public class AtomicBoolean implements java.io.Serializable {
private static final long serialVersionUID = 1L;
// Acesso direto à memória para operações de baixo nível
private static final Unsafe memoryAccessor = Unsafe.getUnsafe();
// Offset de memória do campo em relação ao endereço base do objeto
private static final long fieldMemoryOffset;
// O valor é armazenado como int (0 ou 1) e marcado como volatile
private volatile int internalState;
static {
try {
fieldMemoryOffset = memoryAccessor.objectFieldOffset(
AtomicBoolean.class.getDeclaredField("internalState")
);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public AtomicBoolean(boolean initialState) {
internalState = initialState ? 1 : 0;
}
// Método CAS simplificado
public final boolean compareAndSet(boolean expect, boolean update) {
int expectedInt = expect ? 1 : 0;
int updateInt = update ? 1 : 0;
return memoryAccessor.compareAndSwapInt(this, fieldMemoryOffset, expectedInt, updateInt);
}
}
AtomicInteger e AtomicLong
O AtomicInteger opera diretamente sobre um campo volatile int, enquanto o AtomicLong utiliza um volatile long. Vale ressaltar que, para o AtomicLong, a JVM verifica se a arquitetura subjacente suporta operações CAS de 64 bits nativas através da flag VM_SUPPORTS_LONG_CAS. Caso não suporte, a implementação recorre a mecanismos de fallback com bloqueio.
AtomicReference<V>
Permite a atualização atômica de referências de objetos de qualquer tipo V. É extremamente útil para implementar estruturas de dados lock-free, como filas ou pilhas concurrentes, onde os nós (nodes) precisam ser atualizados atomicamente.
Arrays Atômicos (AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray)
Para manipular arrays de forma atômica, essas classes calculam o endereço de memória exato de cada índice. Isso é feito combinando o endereço base do array com um deslocamento calculado a partir do índice e do tamanho do tipo de dado (scale).
public class AtomicIntegerArray implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private static final Unsafe unsafeEngine = Unsafe.getUnsafe();
// Endereço de memória do primeiro elemento do array
private static final int baseOffset = unsafeEngine.arrayBaseOffset(int[].class);
// Fator de deslocamento bit a bit (shift) baseado no tamanho do elemento
private static final int bitShift;
private final int[] dataStore;
static {
int elementScale = unsafeEngine.arrayIndexScale(int[].class);
if ((elementScale & (elementScale - 1)) != 0) {
throw new Error("O tamanho do tipo de dado não é uma potência de dois");
}
// Calcula o shift: para int (4 bytes), o shift será 2
bitShift = 31 - Integer.numberOfLeadingZeros(elementScale);
}
public AtomicIntegerArray(int length) {
dataStore = new int[length];
}
public final int fetchValue(int index) {
return readRaw(calculateByteOffset(index));
}
private int readRaw(long offset) {
return unsafeEngine.getIntVolatile(dataStore, offset);
}
private long calculateByteOffset(int index) {
if (index < 0 || index >= dataStore.length) {
throw new IndexOutOfBoundsException("Índice fora dos limites: " + index);
}
// Deslocamento = base + (índice * tamanho_do_elemento)
return ((long) index << bitShift) + baseOffset;
}
}
- Atualizadores de Campos Atômicos Baseados em Reflexão
Em cenários onde múltiplas instâncias de uma classe possuem um campo numérico que precisa ser atualizado atomicamente, criar um objeto AtomicInteger para cada instância geraria um overhead de memória significativo. Os "Field Updaters" resolvem isso permitindo a atualização atômica direta de campos volatile específicos de uma classe, utilizando reflexão e a instância do próprio objeto.
AtomicIntegerFieldUpdater e AtomicLongFieldUpdater
Estes atualiazdores premitem modificar campos int e long respectivamente. O campo alvo deve ser obrigatoriamente declarado como volatile e não pode ser final.
public class PlayerScore {
// O campo deve ser volatile para garantir visibilidade e funcionar com o Updater
public volatile int currentScore;
public static void main(String[] args) {
// Cria o atualizador para a classe e o campo específicos
AtomicIntegerFieldUpdater<PlayerScore> scoreUpdater =
AtomicIntegerFieldUpdater.newUpdater(PlayerScore.class, "currentScore");
PlayerScore player = new PlayerScore();
// Atualização atômica sem a necessidade de instanciar um AtomicInteger
scoreUpdater.addAndGet(player, 15);
scoreUpdater.compareAndSet(player, 15, 30);
System.out.println("Pontuação final: " + player.currentScore);
}
}
AtomicReferenceFieldUpdater
Funciona sob a mesma premissa, mas é destinado a campos que armazenam referências de objetos. É amplamente utilizado em frameworks de baixo nível e estruturas de dados concorrentes para atualizar referências de nós (como o ponteiro next em uma lista encadeada) sem alocar objetos atômicos extras.
public abstract class NodeContainer<T> {
// Referência volátil para o próximo nó
public volatile NodeContainer<T> nextNode;
private static final AtomicReferenceFieldUpdater<NodeContainer, NodeContainer> NEXT_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(NodeContainer.class, NodeContainer.class, "nextNode");
public boolean linkNext(NodeContainer<T> newNext) {
return NEXT_UPDATER.compareAndSet(this, null, newNext);
}
}