AtomicMarkableReference<V> can be used to update a reference variable of type V and a boolean atomically together.
Example
AtomicMarkableReference can be used for various purposes. According to Java API doc:
The AtomicMarkableReference class associates a single boolean with a reference. For example, this bit might be used inside a data structure to mean that the object being referenced has logically been deleted.
Let's see an example on that:
package com.logicbig.example;
import java.math.BigDecimal;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;
public class DeleteAtomicMarkableRefExample {
private static class DataInfo {
BigDecimal bd = BigDecimal.TEN;
//obj reference with delete flag (initially false)
AtomicMarkableReference<BigDecimal> numDeleteRef =
new AtomicMarkableReference<>(bd, false);
void delete() {
//after physically deleting from backend mark it deleted
numDeleteRef.compareAndSet(bd, null, false, true);
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10000; i++) {//repeat various times to see thread overlapping effects
DataInfo dataInfo = new DataInfo();
ExecutorService es = Executors.newFixedThreadPool(2);
//deleter
es.execute(() -> {
dataInfo.delete();
});
es.execute(() -> {
boolean[] marked = new boolean[1];
BigDecimal data = dataInfo.numDeleteRef.get(marked);
if (!marked[0] && data == null) {//if 'not deleted' then data should not be null
System.out.printf("deleted: %s data=%s%n", marked[0], data);
}
});
es.shutdown();
es.awaitTermination(10, TimeUnit.MINUTES);
}
System.out.println("finished");
}
} finished
Running above example multiple times never printed anything in the reader thread. That will only happen if setting delete mark to true and reference object to null is not done atomically. Let's see a corresponding example without AtomicMarkableReference.
Without AtomicMarkableReference
package com.logicbig.example;
import java.math.BigDecimal;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class DeleteWithNoAtomicMarkableRefExample {
private static class DataInfo {
private BigDecimal data = BigDecimal.TEN;
private boolean deleted = false;
void delete() {
//after physically deleting from backend mark it deleted
data = null;
deleted = true;
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
DataInfo dataInfo = new DataInfo();
ExecutorService es = Executors.newFixedThreadPool(2);
//deleter
es.execute(() -> {
dataInfo.delete();
});
//reader
es.execute(() -> {
boolean deleted = dataInfo.deleted;
BigDecimal data = dataInfo.data;
if (!deleted && data == null) {//if 'not deleted' then data should not be null
System.out.printf("deleted: %s DataInfo.data=%s%n", deleted, data);
}
});
es.shutdown();
es.awaitTermination(10, TimeUnit.MINUTES);
}
System.out.println("finished");
}
} deleted: false DataInfo.data=null finished
Setting initialized flag for a piece fo data
Let's see another example, where we want to initialize a variable lazily only once. First see how we will do that without AtomicMarkableReference .
Without AtomicMarkableReference
public class InitWithNoAtomicMarkableRefExample {
private static class Test {
private boolean initialized;
private String data;
public void setData(String data) {
if (!initialized) {
this.data = data;
initialized = true;
}
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10000; i++) {
Test test = new Test();
ExecutorService es = Executors.newFixedThreadPool(2);
//writer thread
es.execute(() -> {test.setData("test string");});
//reader thread
es.execute(() -> {
String data = test.data;
boolean initialized = test.initialized;
if (!initialized && data != null) {
System.out.println("not initialized but data: " + data);
}
});
es.shutdown();
es.awaitTermination(10, TimeUnit.MINUTES);
}
System.out.println("finished");
}
} not initialized but data: test string finished
Running above example printed the string which should have never been printed. We expect if initialized=false then data = null. Let's fix that with AtomicMarkableReference .
With AtomicMarkableReference
public class InitAtomicMarkableRefExample {
private static class Test {
private AtomicMarkableReference<String> ref =
new AtomicMarkableReference<>(null, false);
public void setData(String data) {
ref.compareAndSet(null, data, false, true);
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10000; i++) {
Test test = new Test();
ExecutorService es = Executors.newFixedThreadPool(2);
es.execute(() -> {test.setData("test string");});
es.execute(() -> {
boolean[] marked = new boolean[1];
String s = test.ref.get(marked);
if (!marked[0] && s != null) {
System.out.println("not initialized but data: " + s);
}
});
es.shutdown();
es.awaitTermination(10, TimeUnit.MINUTES);
}
System.out.println("finished");
}
} finished
Example ProjectDependencies and Technologies Used:
|