Close

Java - AtomicMarkableReference Example

[Updated: May 23, 2018, Created: May 23, 2018]

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 Project

Dependencies and Technologies Used:

  • JDK 10
  • Maven 3.3.9

AtomicMarkableReference Example Select All Download
  • atomic-markable-reference-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • DeleteAtomicMarkableRefExample.java

    See Also