Brief Introduction to REST
November 9th, 2011
This is from a guest lecture that I delivered to a web-programming class at Bethel University this past month.
Some highly concurrent work I recently did got me thinking about the various methods Java provides for synchronizing access to data. I decided to do some testing to see how they compare. The test code is at the bottom of the post.
I want to start with a few caveats:
I tested the following synchronization methods: synchronized keyword, volatile field, AtomicInteger, Lock, and fair Lock. The test is fairly simple. I ran various numbers of threads that increment counters using the different synchronization methods. Each thread enters a critical section, increments a counter, and exits the critical section in a tight loop so contention is very high. Each synchronization method has its own counter. The result is the time, in milliseconds, it takes the counter to increment to 10 million. I used the following scenarios with various numbers of threads:
The test results are below. My hardware and JVM version are shown in the results. I have a single CPU with 4 hyper-threaded cores so up to 8 threads may be running at a time.
Initializing test...
Mac OS X (i386) version 10.6.8
java.version "1.6.0_29"
Java(TM) SE Runtime Environment (1.6.0_29-b11-402-10M3527)
Java HotSpot(TM) Client VM (build 20.4-b02-402, mixed mode)
JVM Stabilization Count Limit: 10000000
Test Count Limit: 10000000
Threads Syncronized Volatile Atomic Lock FairLock Serial Concurrent
1 236 91 116 289 321 734
2 2195 604 577 922 29686 4461
3 2197 603 876 564 4676
4 2451 965 1071 567 5093 864
5 2401 1006 1118 588 5199
6 2341 1037 1221 592 5113
7 2398 1038 1343 592 5389
8 2378 1048 1451 600 5527 2283
9 2399 1163 1449 605 5699
12 2364 1465 1383 618 6128 2537
24 2464 2957 1431 605 7817 2557
48 2486 5604 1355 611 10008 2755
96 2487 10849 1182 596 16793 3192
Test complete
The results are not hard to interpret so I won’t go through them in detail. Here are the main things I noted:
If you find different performance on other JVMs or hardware please post a comment. Also let me know if you find a bug in the test code.
Here’s the test code:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SyncTest {
private enum TestType {
SYNCHRONIZED, VOLATILE, ATOMIC, LOCK, FAIR_LOCK
}
private final Lock lock = new ReentrantLock(false);
private final Lock fairLock = new ReentrantLock(true);
private int synchronizedCounter;
private int fairLockCounter;
private int lockCounter;
private volatile int volatileCounter;
private final AtomicInteger atomicCounter = new AtomicInteger();
public static void main(String[] args) throws Exception {
new SyncTest().run();
}
public void run() throws Exception {
System.out.println("Initializing test...");
System.out.println();
System.out.printf("%s (%s) version %s n", System.getProperty("os.name"), System.getProperty("os.arch"),
System.getProperty("os.version"));
System.out.println();
System.out.printf("java.version "%s"n", System.getProperty("java.version"));
System.out.printf("%s (%s)n", System.getProperty("java.runtime.name"),
System.getProperty("java.runtime.version"));
System.out.printf("%s (build %s, %s)n", System.getProperty("java.vm.name"),
System.getProperty("java.vm.version"), System.getProperty("java.vm.info"));
System.out.println();
int jvmStabilizationEndValue = 100000;
int endValue = 10000000;
System.out.println("JVM Stabilization Count Limit: " + endValue);
System.out.println("Test Count Limit: " + endValue);
System.out.println();
// run to let JVM do any optimizations and stabilize
runIndividualTest(TestType.SYNCHRONIZED, 1, jvmStabilizationEndValue);
runIndividualTest(TestType.VOLATILE, 1, jvmStabilizationEndValue);
runIndividualTest(TestType.ATOMIC, 1, jvmStabilizationEndValue);
runIndividualTest(TestType.LOCK, 1, jvmStabilizationEndValue);
runTestsConcurrently(1, jvmStabilizationEndValue);
runTestsSerially(1, jvmStabilizationEndValue);
System.out
.printf("Threads Syncronized Volatile Atomic Lock FairLock Serial Concurrentn");
runAllTests(1, endValue);
runAllTests(2, endValue);
runAllTests(3, endValue);
runAllTests(4, endValue);
runAllTests(5, endValue);
runAllTests(6, endValue);
runAllTests(7, endValue);
runAllTests(8, endValue);
runAllTests(9, endValue);
runAllTests(12, endValue);
runAllTests(24, endValue);
runAllTests(48, endValue);
runAllTests(96, endValue);
System.out.println("n Test complete");
}
private void runAllTests(int threadCount, int endValue) throws Exception {
long synchronizedElapsed = runIndividualTest(TestType.SYNCHRONIZED, threadCount, endValue);
long volatileElapsed = runIndividualTest(TestType.VOLATILE, threadCount, endValue);
long atomicElapsed = runIndividualTest(TestType.ATOMIC, threadCount, endValue);
long lockElapsed = runIndividualTest(TestType.LOCK, threadCount, endValue);
long serialElapsed = runTestsSerially(threadCount, endValue);
long concurrenteElapsed = runTestsConcurrently(threadCount, endValue);
if (concurrenteElapsed > 0) {
System.out.printf("%7d %11d %11d %11d %11d %11s %11d %11dn", threadCount, synchronizedElapsed,
volatileElapsed, atomicElapsed, lockElapsed, "", serialElapsed, concurrenteElapsed);
} else if (threadCount <= 2) {
long fairLockElapsed = runIndividualTest(TestType.FAIR_LOCK, threadCount, endValue);
System.out.printf("%7d %11d %11d %11d %11d %11d %11dn", threadCount, synchronizedElapsed, volatileElapsed,
atomicElapsed, lockElapsed, fairLockElapsed, serialElapsed);
} else {
System.out.printf("%7d %11d %11d %11d %11d %11s %11dn", threadCount, synchronizedElapsed, volatileElapsed,
atomicElapsed, lockElapsed, "", serialElapsed);
}
}
private long runIndividualTest(final TestType testType, int threadCount, final int endValue) throws Exception {
final CyclicBarrier testsStarted = new CyclicBarrier(threadCount + 1);
final CountDownLatch testsComplete = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
startTestThread(testType, testsStarted, testsComplete, endValue);
}
return waitForTests(testsStarted, testsComplete);
}
private long runTestsSerially(int threadCount, final int endValue) throws Exception {
final CyclicBarrier testsStarted = new CyclicBarrier(threadCount + 1);
final CountDownLatch testsComplete = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
Thread t = new Thread() {
public void run() {
try {
testsStarted.await();
runSynchronizedTest(endValue);
runVolatileTest(endValue);
runAtomicTest(endValue);
runLockTest(endValue);
} catch (Throwable t) {
t.printStackTrace();
} finally {
testsComplete.countDown();
}
}
};
t.start();
}
return waitForTests(testsStarted, testsComplete);
}
private long runTestsConcurrently(int threadCount, int endValue) throws Exception {
if (threadCount % 4 != 0) {
return -1;
}
final CyclicBarrier testsStarted = new CyclicBarrier(threadCount + 1);
final CountDownLatch testsComplete = new CountDownLatch(threadCount);
threadCount /= 4;
for (int i = 0; i < threadCount; i++) {
startTestThread(TestType.SYNCHRONIZED, testsStarted, testsComplete, endValue);
startTestThread(TestType.VOLATILE, testsStarted, testsComplete, endValue);
startTestThread(TestType.ATOMIC, testsStarted, testsComplete, endValue);
startTestThread(TestType.LOCK, testsStarted, testsComplete, endValue);
}
return waitForTests(testsStarted, testsComplete);
}
private void startTestThread(final TestType testType, final CyclicBarrier testsStarted,
final CountDownLatch testsComplete, final int endValue) {
Thread t = new Thread() {
public void run() {
try {
testsStarted.await();
switch (testType) {
case SYNCHRONIZED:
runSynchronizedTest(endValue);
break;
case VOLATILE:
runVolatileTest(endValue);
break;
case ATOMIC:
runAtomicTest(endValue);
break;
case LOCK:
runLockTest(endValue);
break;
case FAIR_LOCK:
runFairLockTest(endValue);
break;
}
} catch (Throwable t) {
t.printStackTrace();
} finally {
testsComplete.countDown();
}
}
};
t.start();
}
private long waitForTests(CyclicBarrier testsStarted, CountDownLatch testsComplete) throws Exception {
testsStarted.await();
long startTime = System.currentTimeMillis();
testsComplete.await();
long endTime = System.currentTimeMillis();
reset();
return endTime - startTime;
}
private void reset() {
synchronized (this) {
synchronizedCounter = 0;
}
volatileCounter = 0;
atomicCounter.set(0);
lock.lock();
try {
lockCounter = 0;
} finally {
lock.unlock();
}
fairLock.lock();
try {
fairLockCounter = 0;
} finally {
fairLock.unlock();
}
}
private void runSynchronizedTest(long endValue) {
boolean run = true;
while (run) {
run = incrementSynchronizedCounter(endValue);
}
}
private synchronized boolean incrementSynchronizedCounter(long endValue) {
return ++synchronizedCounter < endValue;
}
private void runVolatileTest(long endValue) {
boolean run = true;
while (run) {
run = ++volatileCounter < endValue;
}
}
private void runAtomicTest(long endValue) {
boolean run = true;
while (run) {
run = atomicCounter.incrementAndGet() < endValue;
}
}
private void runLockTest(long endValue) {
boolean run = true;
while (run) {
lock.lock();
try {
run = ++lockCounter < endValue;
} finally {
lock.unlock();
}
}
}
private void runFairLockTest(long endValue) {
boolean run = true;
while (run) {
fairLock.lock();
try {
run = ++fairLockCounter < endValue;
} finally {
fairLock.unlock();
}
}
}
}
This is from a guest lecture that I delivered to a web-programming class at Bethel University this past month.
Just last week the Eclipse group released a service release to their Indigo version, including support for Java 7.
Grails gives you the option of overriding the HTTP method via an extra parameter(_method) or a custom HTTP Header(X-HTTP-Method-Override).
Insert bio here