Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a simple thread-safe class using a volatile variable?

I want to write a simple thread-safe class that could be used to set or get an Integer value.

The easiest way is to use the synchronized keyword:

public class MyIntegerHolder {

    private Integer value;

    synchronized public Integer getValue() {
        return value;
    }

    synchronized public void setValue(Integer value) {
        this.value = value;
    }

}

I could also try using volatile:

public class MyIntegerHolder {

    private volatile Integer value;

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }

}

Is the class with the volatile keyword thread-safe?

Consider the following sequence of events:

  1. Thread A sets the value to 5.
  2. Thread B sets the value to 7.
  3. Thread C reads the value.

It follows from the Java Language Specification that

  • "1" happens-before "3"
  • "2" happens-before "3"

but I don't see how it could follow from the specification that "1" happens-before "2" so I suspect that "1" doesn't happen-before "2".

I suspect the thread C may read 7 or 5. I think the class with the volatile keyword is not thread-safe and the following sequence is also possible:

  1. Thread A sets the value to 5.
  2. Thread B sets the value to 7.
  3. Thread C reads 7.
  4. Thread D reads 5.
  5. Thread C reads 7.
  6. Thread D reads 5.
  7. ...

Am I correct in assuming that MyIntegerHolder with volatile is not thread-safe?

Is it possible to make a thread-safe Integer holder by using AtomicInteger:

public class MyIntegerHolder {

    private AtomicInteger atomicInteger = new AtomicInteger();

    public Integer getValue() {
        return atomicInteger.get();
    }

    public void setValue(Integer value) {
        atomicInteger.set(value);
    }

}

?

Here is a fragment of the Java Concurrency In Practice book:

"Reads and writes of atomic variables have the same memory semantics as volatile variables."

What is the best (preferably non-blocking) way of writing a thread-safe MyIntegerHolder?

If you know the answer, I would like to know why you think it is correct. Does it follow from the specification? If so, how?

like image 691
Mateusz Zakrzewski Avatar asked May 03 '13 12:05

Mateusz Zakrzewski


People also ask

Does volatile Make a variable thread-safe?

Volatile keyword is used to modify the value of a variable by different threads. It is also used to make classes thread safe. It means that multiple threads can use a method and instance of the classes at the same time without any problem. The volatile keyword can be used either with primitive type or objects.

How do you write a thread-safe class?

To make these classes thread-safe, you must prevent concurrent access to the internal state of an instance by more than one thread. Because Java was designed with threads in mind, the language provides the synchronized modifier, which does just that.

How do you declare a volatile variable?

To declare a variable volatile, include the keyword volatile before or after the data type in the variable definition.

Is it possible to make an array volatile in Java to write thread-safe?

The answer is, Yes, you can make an array (both primitive and reference type array e.g. an int array and String array) volatile in Java but only changes to reference pointing to an array will be visible to all threads, not the whole array.


2 Answers

The keyword synchronized is saying that if Thread A and Thread B want to access the Integer, they cannot do so simultaneously. A is telling B wait until I'm done with it.

On the other hand, volatile makes threads more "friendly". They start talking to each other and working together to perform tasks. So when B tries to access, A will inform B of everything it has done until that moment. B is now aware of the changes and can continue its job from where A left of.

In Java, you have Atomic for this reason, which under the covers use the volatile keyword, so they are doing pretty much the same thing, but they save you time and effort.

The thing you are looking for is AtomicInteger, you are right about this. For the operation you are trying to perform this is the best choice.

There are two main uses of `AtomicInteger`:

 * As an atomic counter (incrementAndGet(), etc) that can be used by many threads concurrently

 * As a primitive that supports compare-and-swap instruction (compareAndSet()) to implement non-blocking algorithms. 

To answer your question on a general note

It depends on what you need. I'm not saying synchronized is wrong and volatile is good, otherwise the nice Java people would have removed synchronized a long time ago. There is no absolute answer, there are a lot of specific cases and usage scenarios.

A few of my bookmarks:

Concurrency tips

Core Java Concurrency

Java concurrency

Update

From the Java Concurrency specification available here:

Package java.util.concurrent.atomic

A small toolkit of classes that support lock-free thread-safe programming on single variables.

Instances of classes `AtomicBoolean`, `AtomicInteger`, `AtomicLong`, and `AtomicReference` each provide access and updates to a single variable of the corresponding type.
Each class also provides appropriate utility methods for that type.
For example, classes `AtomicLong` and AtomicInteger provide atomic increment methods.

The memory effects for accesses and updates of atomics generally follow the rules for volatiles:

get has the memory effects of reading a volatile variable.
set has the memory effects of writing (assigning) a volatile variable.

Also from Here

The Java programming language volatile keyword:

(In all versions of Java) There is a global ordering on the reads and writes to a volatile variable. This implies that every thread accessing a volatile field will read its current value before continuing, instead of (potentially) using a cached value. (However, there is no guarantee about the relative ordering of volatile reads and writes with regular reads and writes, meaning that it's generally not a useful threading construct.)

like image 91
flavian Avatar answered Oct 22 '22 19:10

flavian


If you need only get / set on a variable it is enough to declare it volatile like you did. If you check how AtomicInteger set / get work you will see the same implementation

private volatile int value;
...

public final int get() {
    return value;
}

public final void set(int newValue) {
    value = newValue;
}

but you cannot increment a volatile field atomically this simple. This is where we use AtomicInteger.incrementAndGet or getAndIncrement methods .

like image 32
Evgeniy Dorofeev Avatar answered Oct 22 '22 19:10

Evgeniy Dorofeev