Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 4535

Guide to AtomicMarkableReference

$
0
0

1. Overview

In this tutorial, we'll dive into the details of the AtomicMarkableReference class from the java.util.concurrent.atomic package.

Next, we'll walk through the API methods of the class, and we'll see how we can use the AtomicMarkableReference class in practice.

2. Purpose

AtomicMarkableReference is a generic class that encapsulates both a reference to an Object and a boolean flag. These two fields are coupled together and can be updated atomically, either together or individually.

3. Implementation

Let's take a more in-depth look at the AtomicMarkableReference class implementation:

public class AtomicMarkableReference<V> {

    private static class Pair<T> {
        final T reference;
        final boolean mark;
        private Pair(T reference, boolean mark) {
            this.reference = reference;
            this.mark = mark;
        }
        static <T> Pair<T> of(T reference, boolean mark) {
            return new Pair<T>(reference, mark);
        }
    }

    private volatile Pair<V> pair;

    // ...
}

Notice that AtomicMarkableReference has a static nested class Pair that holds the reference and flag.

Also, we see that both variables are final. As a result, whenever we want to modify these variables, a new instance of the Pair class is created, and the old instance is replaced.

4. Methods

First of all, to discover AtomicMarkableReference‘s usefulness, let's start by creating an Employee POJO:

class Employee {
    private int id;
    private String name;
    
    // constructor & getters & setters
}

Now, we can create an instance of the AtomicMarkableReference class:

AtomicMarkableReference<Employee> employeeNode = new AtomicMarkableReference<>(new Employee(123, "Mike"), true);

For our examples, let's suppose that our AtomicMarkableReference instance represents a node in an organization chart. It is holding the two variables: the reference to an instance of the Employee class and a mark that indicates if the employee is active or has left the company.

AtomicMarkableReference comes with several methods to update or retrieve either one or both fields. Let's have a look at these methods one by one:

4.1. getReference()

We use the getReference method to return the current value of the reference variable:

Employee employee = new Employee(123, "Mike");
AtomicMarkableReference<Employee> employeeNode = new AtomicMarkableReference<Employee>(employee, true);

Assertions.assertEquals(employee, employeeNode.getReference());

4.2. isMarked()

To get the value of the mark variable, we should call the isMarked method:

Employee employee = new Employee(123, "Mike");
AtomicMarkableReference<Employee> employeeNode = new AtomicMarkableReference<Employee>(employee, true);

Assertions.assertTrue(employeeNode.isMarked());

4.3. get()

Next, we use the get method when we want to retrieve both the current reference and the current mark. To get the mark, we should send as a parameter a boolean array of size at least one, which will store at index 0 the current value of the boolean variable. At the same time, the method will return the current value of the reference:

Employee employee = new Employee(123, "Mike");
AtomicMarkableReference<Employee> employeeNode = new AtomicMarkableReference<Employee>(employee, true);

boolean[] markHolder = new boolean[1];
Employee currentEmployee = employeeNode.get(markHolder);

Assertions.assertEquals(employee, currentEmployee);
Assertions.assertTrue(markHolder[0]);

This way of getting both the reference and the mark fields is a little odd because the inner Pair class is not exposed to the caller.

Java doesn't have a generic Pair<T, U> class in its public API. The main reason for this is that we may be tempted to overuse it instead of creating distinct types.

4.4. set()

In case we want to update both the reference and the mark fields unconditionally, we should use the set method. If at least one of the values sent as a parameter is different, the reference and the mark will be updated:

Employee employee = new Employee(123, "Mike");
AtomicMarkableReference<Employee> employeeNode = new AtomicMarkableReference<Employee>(employee, true);

Employee newEmployee = new Employee(124, "John");
employeeNode.set(newEmployee, false);
        
Assertions.assertEquals(newEmployee, employeeNode.getReference());
Assertions.assertFalse(employeeNode.isMarked());

4.5. compareAndSet()

Next, the compareAndSet method updates both the reference and the mark to the given updated values if the current reference is equal to the expected reference, and the current mark is equal to the expected mark.

Before seeing compareAndSet in action, let's have a look at the method's signature:

boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark)

Now, let's see how we can update both reference and mark fields using compareAndSet:

Employee employee = new Employee(123, "Mike");
AtomicMarkableReference<Employee> employeeNode = new AtomicMarkableReference<Employee>(employee, true);
Employee newEmployee = new Employee(124, "John");

Assertions.assertTrue(employeeNode.compareAndSet(employee, newEmployee, true, false));
Assertions.assertEquals(newEmployee, employeeNode.getReference());
Assertions.assertFalse(employeeNode.isMarked());

Also, when calling the compareAndSet method, we get true if the fields were updated or false if the update failed.

4.6. weakCompareAndSet()

Then, the weakCompareAndSet method has the same signature and behavior as the compareAndSet method. By taking a look at its internal implementation, we see that the weakCompareAndSet method is just calling the compareAndSet method.

Therefore, we should prefer the compareAndSet method to avoid the additional method call.

4.7. attemptMark()

Finally, we have the attemptMark method. It checks whether the current reference is equal to an expected reference sent as a parameter. If they match, it sets the value of the mark atomically to the given updated value:

Employee employee = new Employee(123, "Mike");
AtomicMarkableReference<Employee> employeeNode = new AtomicMarkableReference<Employee>(employee, true);

Assertions.assertTrue(employeeNode.attemptMark(employee, false));
Assertions.assertFalse(employeeNode.isMarked());

It's important to note that this method may fail spuriously even if the expected and current reference are equal. As a result, we should pay attention to the boolean returned by the method execution.

The result is true if the mark was updated successfully, or false otherwise. However, repeated invocation when the current reference is equal to the expected reference will modify the mark value. As a result, it is advisable to use this method inside a while loop structure.

This failure may occur as a result of the underlying compare-and-swap (CAS) algorithm used by the attemptMark method to update the fields. If we have multiple threads that are trying to update the same value using CAS, one of them manages to change the value, and the other ones are notified that the update failed.

5. Conclusion

In this quick guide, we learned how the AtomicMarkableReference class is implemented. Moreover, we discovered how we could update its properties atomically by going through the class's public API methods.

As always, more examples and the full source code of the article are available over on GitHub.


Viewing all articles
Browse latest Browse all 4535

Trending Articles