It has been some time since the release of Java 5. It introduced a number of improvements in terms of classes to handle concurrency concerns. Today I will cruising thru the java.util.concurrent.atomic package and looking at the package’s relevance in tackling real-life issues.
Let’s get started with the most basic requirements. Assume that you have a web site that maintains the number of users accessing a page. The counter information is not persisted beyond the web site up time. Therefore, we just need a counter to maintain a count of the number of users accessing a page. This can be achieved by creating a counter which is incremented on every page access. To ensure precise measurement, the counter needs to be incremented by a single thread and no other thread should access the counter variable. In pre Java 5 days, we would have to control access to the counter variable using a synchronized block. Here’s the sample code to do it:
class Counter {
private int count = 0;
Counter () {
}
synchronized void increment() {
count++;
}
int getCount() {
return count;
}
}
The synchronized is an all or nothing block. Java 5 provides an improved solution to handle the problem instead use the java.util.concurrent.atomic.AtomicInteger. This class implements the behavior as expected from Counter and in a more efficient manner. I have created the following test codes to verify the results:
public class CounterTest {
public static void main(String[] args) {
Counter counter = new Counter();
for(int i=0; i< 1600; i++) {
//This loop to get rid of any hotspot optimizations
counter.increment();
}
long startTime = System.nanoTime();
for(int i=0; i< 150000; i++) {
counter.increment();
}
long endTime = System.nanoTime();
double processingTime = (endTime - startTime)/Math.pow(10, 6);
System.out.println("Processing Time (msec): " + processingTime + " Count: " + counter.getCount());
}
}
public class AtomicIntegerTest {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(0);
for(int i=0; i< 1600; i++) {
//This loop to get rid of any hotspot optimizations
counter.incrementAndGet();
}
long startTime = System.nanoTime();
for(int i=0; i< 150000; i++) {
counter.incrementAndGet();
}
long endTime = System.nanoTime();
double processingTime = (endTime - startTime)/Math.pow(10, 6);
System.out.println("Processing Time (msec): " + processingTime + " Count: " + counter.intValue());
}
}
Running CounterTest displays the following: Processing Time (msec): 18.196106 Count: 151600 Running AtomicIntegerTest displays the following: Processing Time (msec): 11.212189 Count: 151600
public class ConcurrencyTest {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(0);
for(int i=0; i<100; i++) {
Runnable runnable = new WorkerThread(counter);
Thread t = new Thread(runnable);
t.start();
}
}
}
class WorkerThread implements Runnable {
AtomicInteger count = null;
WorkerThread(AtomicInteger counter) {
this.count = counter;
}
public void run() {
int value = this.count.incrementAndGet();
System.out.println(value);
}
}
There are AtomicIntegerArray and AtomicLongArray classes.Apparently there is no AtomicFloat or AtomicDouble. The reason is explained in a foot note in the package summary.
You can also hold floats using Float.floatToIntBits and Float.intBitstoFloat conversions, and doubles using Double.doubleToLongBits and Double.longBitsToDouble conversions.
0 responses so far ↓
There are no comments yet...Kick things off by filling out the form below.