Clase atómica
Tabla de contenido
1. Clasificación atómica
Las clases atómicas están básicamente en el paquete java.util.concurrent.atomic
1.1 Tipos básicos de átomos
- AtómicoBooleano
- AtomicInteger
- AtomicLong
1.2 Tipos de matrices atómicas
- AtomicIntegerArray
- AtomicLongArray
1.3 Tipos de referencia atómica
- AtomicReference
- AtomicStampedReference
1.4 Tipos de atributos atómicos
- AtomicLongFieldUpdater
- AtomicIntegerFieldUpdater
2. Instancia no atómica
Código de prueba
public class Test {
static Long totalCount = 0L;
public static void main(String[] args) {
int j = 0;
while (j < 100) {
totalCount = 0L;
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
totalCount++;
}
}
}, "线程1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
totalCount++;
}
}
}, "线程1");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
System.out.println("当前总数量为:" + totalCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
j++;
}
}
}
Resultados de la prueba
当前总数量为:884
当前总数量为:717
当前总数量为:563
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:686
当前总数量为:1000
...
Conclusión: se puede observar que cuando las clases no atómicas son multiproceso, los resultados obtenidos no son los que queremos
3. Instancia de clase atómica
Código de prueba
public class Test {
// static Long totalCount = 0L;
static AtomicInteger totalCount;
public static void main(String[] args) {
int j = 0;
while (j < 100) {
//原子类,初始值为0
totalCount = new AtomicInteger(0);
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
//totalCount++;
totalCount.getAndIncrement();
}
}
}, "线程1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
//totalCount++;
totalCount.getAndIncrement();
}
}
}, "线程1");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
// System.out.println("当前总数量为:" + totalCount);
System.out.println("当前总数量为:" + totalCount.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
j++;
}
}
}
Resultados de la prueba
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
Conclusión: AtomicInteger puede garantizar la atomicidad de los datos en el caso de multiproceso
4. Métodos comunes de AtomicInteger
- getAndIncrement se usa primero y luego se incrementa en 1, que es equivalente a n ++
- getAndDecrement se usa primero y luego se reduce en 1, que es equivalente a n–
- DecrementAndGet primero decrementar en 1, luego usar, equivalente a -n
- incrementAndGet se incrementa en 1 y está en uso. Equivalente a ++ n
- obtener Obtener el valor actual
4. Análisis del código fuente de AtomicInteger
Utilice getAndAddInt del método inseguro para incrementar automáticamente
esto: es el valor actual valor
Desplazamiento de la dirección de compensación, para ayudarlo a encontrar la dirección de n en el montón
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
getIntVolatile Obtiene el valor esperado directamente de la memoria a través de volatile: var5
var1: valor actual
var2: dirección offset
var5: valor esperadocompareAndSwapInt Realice la operación CAS
var1: valor actual
var2: desplazamiento de dirección
var4: intervalo de incremento
var5: valor esperado
Compare si var1 y var5 son iguales,
iguales, lo que significa que ningún hilo ha cambiado, valor actual (var1) = valor esperado (var5 ) + El intervalo de incremento (var4) devuelve verdadero
no es el mismo, significa que el hilo ha cambiado, el valor actual (var1) = valor esperado (var5) devuelve falso, inténtelo de nuevo
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
5. Problema de ABA
Porque
- El hilo 1 lee var1 = A en la memoria del hilo (pila),
- El hilo 2 cambia var1 a B y luego a A
- Cuando el hilo 1 obtiene el valor esperado var5, sigue siendo A, lo que está en línea con las expectativas, lo que resulta en que no se cambie el reembolso
- En realidad, se ha cambiado el hilo 2
6. Solución ABA
La solución es agregar una marca de tiempo o un número de versión a la variable. En este caso, si se cambia, la marca de tiempo o el número de versión cambiará, y se puede encontrar en CAS
AtomicStampedReference resuelve el problema de ABA
getReference obtiene el valor esperado
getStamp obtiene la marca de tiempo
Código de prueba
public class ABATest {
private static AtomicStampedReference<Integer> totalCount;
public static void main(String[] args) {
int j = 0;
while (j < 100) {
//原子类,初始值为0
totalCount = new AtomicStampedReference<>(0, 0);
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
//totalCount++;
Integer reference;
int stamp;
do {
reference = totalCount.getReference();
stamp = totalCount.getStamp();
} while (!totalCount.compareAndSet(reference, reference + 1, stamp, stamp + 1));
}
}
}, "线程1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
Integer reference;
int stamp;
do {
reference = totalCount.getReference();
stamp = totalCount.getStamp();
} while (!totalCount.compareAndSet(reference, reference + 1, stamp, stamp + 1));
}
}
}, "线程1");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
// System.out.println("当前总数量为:" + totalCount);
System.out.println("当前总数量为:" + totalCount.getReference());
} catch (InterruptedException e) {
e.printStackTrace();
}
j++;
}
}
}
Resultados de la prueba
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000