Run
below program and notice that count value varies between 9, 10, 11 to 18. The
reason is because count++ is not an atomic operation. So by the time one
threads read its value and increment it by one, other
thread has read the older value leading to wrong result.
class Worker implements Runnable {
private int
count;
@Override
public void
run() {
for (int
i = 1; i < 10; i++) {
process(i);
count++;
}
}
public int
getCount() {
return this.count;
}
private void
process(int i) {
try {
Thread.sleep(i * 200);
} catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
public class TestThreads {
public static void main(String[] args) throws InterruptedException {
Worker pt = new Worker();
Thread t1 = new Thread(pt, "t1");
t1.start();
Thread t2 = new Thread(pt, "t2");
t2.start();
t1.join();
t2.join();
System.out.println("Count=" + pt.getCount());
}
}
To
solve this issue, we will have to make sure that increment operation on count
is atomic, we can do that using Synchronization however Java 5 java.util.concurrent.atomic
provides wrapper classes for int and long that can be used to achieve
this atomic operation without usage of Synchronization.
The
java.util.concurrent.atomic package defines classes that support atomic
operations on single variables. All classes have get and set methods that work
like reads and writes on volatile variables. That is, a set has happens-before
relationship with any subsequent get on the same variable.
The
atomic compareAndSet method also has these memory consistency features, as do
the simple atomic arithmetic methods that apply to integer atomic variables.
import java.util.concurrent.atomic.AtomicInteger;
class Worker implements Runnable {
private AtomicInteger count = new
AtomicInteger();
@Override
public void
run() {
for (int
i = 1; i < 10; i++) {
process(i);
count.incrementAndGet();
}
}
public int
getCount() {
return this.count.get();
}
private void
process(int i) {
try {
Thread.sleep(i * 200);
} catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
No comments:
Post a Comment