2

Hi I am reading java concurrency in practice and I read interesting statement states that

Locking can guarantee both visibility and atomicity; volatile variables can only guarantee visibility.

Can any one please explain that if declaring a variable as volatile all other reading threads getting updated values so why do i care about the atomicity in statement like: counter = counter + 1;

Thanks in advance.

5
  • check out this post. stackoverflow.com/questions/3519664/… Commented Dec 18, 2017 at 10:06
  • 1
    "Why do i care about the atomicity in statement like counter = counter + 1;". Well if you want your counter to have correct values, you should care. Commented Dec 18, 2017 at 10:14
  • @so_what : Please mark the answer right if it helps you Commented Dec 18, 2017 at 10:19
  • One last doubt which troubles me that should i think atomic variables=volatile(for get recent write value)+synchronized(to ensure atomicity) ? Commented Dec 18, 2017 at 10:26
  • If we didn't have the atomic classes (which was the case before jdk 1.5) we would have to use volatile+synchronized. But synchronization has a performance overhead. The atomic classes are more efficient for this purpose. See docs.oracle.com/javase/tutorial/essential/concurrency/… Commented Dec 18, 2017 at 11:54

2 Answers 2

3

The effect of the volatile keyword is approximately that each individual read or write operation on that variable is atomic.

Notably, however, an operation that requires more than one read/write -- such as i++, which is equivalent to i = i + 1, which does one read and one write -- is not atomic, since another thread may write to i between the read and the write.

The Atomic classes, like AtomicInteger and AtomicReference, provide a wider variety of operations atomically, specifically including increment for AtomicInteger.

That's why you need to care about atomicity in statements like counter = counter + 1

Please check this post Volatile Vs Atomic

Sign up to request clarification or add additional context in comments.

5 Comments

This answer seems to state how to fix the problems caused by these non-atomic operations. But it doesn't answer the question of why it's a problem to begin with. Namely in the case of a variable being incremented in two threads can result in only a single increment if both threads read the same value before incrementing
"Notably, however, an operation that requires more than one read/write -- such as i++, which is equivalent to i = i + 1, which does one read and one write -- is not atomic, since another thread may write to i between the read and the write" Does it not answer the question of why does atomicity needs to be cared ? Please read it clearly before concluding anything @Cruncher
"another thread may write to i between the read and the write" statement makes sense for me because I have done demo on this but can you consider the following use cases for thread-safe: 1)Volatile +atomic variables(not individauly)=perfect 2)volatile+synchronized block=perfect 3)individual atmoic operation+synchronized=perfect
@Aarish That's actually just restated the definition of being non-atomic. If I was new to thread safety I'd have many questions about what that actually means. why is it a problem if threads read between the read and write?
@Cruncher It is a problem if threads write between read and write and not with reads
2

Here's a self-contained example executable application that demonstrates that volatile on its own is not enough. Four threads increment a counter 10,000 times each, so you'd expect the counter to be 40,000 at the end. It uses a primitive int variable and an AtomicInt, and tries the exercise 5 times each.

import java.util.Collections; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; class AtomicDemo { interface Demo extends Callable<Void> { int getCounter(); } static class UsePrimitive implements Demo { private volatile int counter = 0; public Void call() throws Exception { for (int i = 1; i <= 10000; ++i) { ++counter; } return null; } public int getCounter() { return counter; } } static class UseAtomic implements Demo { final AtomicInteger counter = new AtomicInteger(0); public Void call() throws Exception { for (int i = 1; i <= 10000; ++i) { counter.incrementAndGet(); System.out.print(""); } return null; } public int getCounter() { return counter.get(); } } public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newFixedThreadPool(4); for (int i = 1; i <= 5; ++i) { Demo demo = new UsePrimitive(); exec.invokeAll(Collections.nCopies(4, demo)); System.out.println("Count to 40000 using primitive, attempt number " + i + ": " + demo.getCounter()); } for (int i = 1; i <= 5; ++i) { Demo demo = new UseAtomic(); exec.invokeAll(Collections.nCopies(4, demo)); System.out.println("Count to 40000 using atomic, attempt number " + i + ": " + demo.getCounter()); } exec.shutdownNow(); } } 

Typical output:

Count to 40000 using primitive, attempt number 1: 39711 Count to 40000 using primitive, attempt number 2: 39686 Count to 40000 using primitive, attempt number 3: 39972 Count to 40000 using primitive, attempt number 4: 39840 Count to 40000 using primitive, attempt number 5: 39865 Count to 40000 using atomic, attempt number 1: 40000 Count to 40000 using atomic, attempt number 2: 40000 Count to 40000 using atomic, attempt number 3: 40000 Count to 40000 using atomic, attempt number 4: 40000 Count to 40000 using atomic, attempt number 5: 40000 

You see, only with AtomicInt do you always get the expected results.

1 Comment

great I tried also and come to conclusion that volatile is only for getting the latest value for the thread not prevent violation of atomicity ,if context switching happen at some unfortunate time then we will not get the correct value.Also when using Atomicxxx classes they are using volatile variable for get the laatest value and then uses synchronization to update the value correctly.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.