In JDK5, old synchornized keyword is much slower than ReentrantLock and Semaphore implementation. It is worth to mention that the spinlock is the fastest.
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
public class SyncExample {
// Thread starting latch
CountDownLatch startSignal;
int dummy;
// old synchornized keyword
public void synchornized_method(int n) {
try { startSignal.await(); } catch(InterruptedException ie) { ie.printStackTrace(); }
synchronized(this) {
System.out.println(dummy + “+” + n + “=” + (dummy+n));
dummy++;
}
}
// lock approach
private ReentrantLock lock = new ReentrantLock();
public void lock_method(int n) {
try { startSignal.await(); } catch(InterruptedException ie) { ie.printStackTrace(); }
lock.lock();
try {
System.out.println(dummy + “+” + n + “=” + (dummy+n));
dummy++;
}
finally {
lock.unlock();
}
}
// semaphore approach
private Semaphore s = new Semaphore(1);
public void semaphore_method(int n) {
try { startSignal.await(); } catch(InterruptedException ie) { ie.printStackTrace(); }
try {
s.acquire();
System.out.println(dummy + “+” + n + “=” + (dummy+n));
dummy++;
}
catch(InterruptedException ie) {
ie.printStackTrace();
}
finally {
s.release();
}
}
// spin lock approach
// to get dummy value from memory, we need to make it atomic
AtomicInteger ai_dummy;
public void spin_lock(int n) {
int local_dummy = ai_dummy.getAndIncrement();
System.out.println(local_dummy + “+” + n + “=” + (local_dummy + n));
}
private void waitSomeTime() {
try { Thread.sleep(1000); } catch(InterruptedException ie) {}
}
private void waitLittleTime() {
try { Thread.sleep(10); } catch(InterruptedException ie) {}
}
public void syncTest() {
startSignal = new CountDownLatch(1);
dummy = 0;
ExecutorService exec = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
final int j = i;
exec.submit(new Runnable() {
public void run() {
synchornized_method(j);
}
});
}
// no new thread
exec.shutdown();
// give some time to make all threads started
waitSomeTime();
// now, time the execs
long start = System.currentTimeMillis();
// give start signals
startSignal.countDown();
while (true) { if (exec.isTerminated()) break; waitLittleTime(); }
System.out.println(“Elapsed time for synchornized: “ + (System.currentTimeMillis() - start));
}
public void lockTest() {
startSignal = new CountDownLatch(1);
dummy = 0;
ExecutorService exec = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
final int j = i;
exec.submit(new Runnable() {
public void run() {
lock_method(j);
}
});
}
// no new thread
exec.shutdown();
// give some time to make all threads started
waitSomeTime();
// now, time the execs
long start = System.currentTimeMillis();
// give start signals
startSignal.countDown();
while (true) { if (exec.isTerminated()) break; waitLittleTime(); }
System.out.println(“Elapsed time for lock: “ + (System.currentTimeMillis() - start));
}
public void semaTest() {
startSignal = new CountDownLatch(1);
dummy = 0;
ExecutorService exec = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
final int j = i;
exec.submit(new Runnable() {
public void run() {
semaphore_method(j);
}
});
}
// no new thread
exec.shutdown();
// give some time to make all threads started
waitSomeTime();
// now, time the execs
long start = System.currentTimeMillis();
// give start signals
startSignal.countDown();
while (true) { if (exec.isTerminated()) break; waitLittleTime(); }
System.out.println(“Elapsed time for semaphore: “ + (System.currentTimeMillis() - start));
}
public void spinLockTest() {
startSignal = new CountDownLatch(1);
ai_dummy = new AtomicInteger(0);
ExecutorService exec = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
final int j = i;
exec.submit(new Runnable() {
public void run() {
spin_lock(j);
}
});
}
// no new thread
exec.shutdown();
// give some time to make all threads started
waitSomeTime();
// now, time the execs
long start = System.currentTimeMillis();
// give start signals
startSignal.countDown();
while (true) { if (exec.isTerminated()) break; waitLittleTime(); }
System.out.println(“Elapsed time for spinlock: “ + (System.currentTimeMillis() - start));
}
public static void main(String[] args) {
SyncExample se = new SyncExample();
for (int i = 0; i < 10; i++) {
se.syncTest();
se.lockTest();
se.semaTest();
se.spinLockTest();
se.waitSomeTime();
}
}
}
Because the ‘works’ done by threads was way too simple, I did not have to spin,i.e., while (true). But the situation is not much different even if your work is more quite complex than above.
Note: The following conclusion is incorrect. Please read the comments of this article.
I’ve computed trimmed mean (10%) of execution times and the result is as follows:
Synchornized: 1139.4 ms
Lock: 817.7 ms
Semaphore: 823.8 ms
Spinlock: 1.1 ms
Which obviously proves the slowness of synchornized keyword. If the system performance is your primary concerns,
use ReentrantLock, Semaphore, and Spinlock. But be aware that you must release locks&semaphores in finally clauses.
Comments 3
The following line is missing in the Spinlock case, which makes the conclusion irrelevant.
I tested on my own machine and get following outcome:
for synchronized: 2300
Posted 21 Dec 2011 at 6:03 am ¶for lock: 2201
for semaphore: 2305
for spinlock: 2304
Sorry code did not get through
“try { startSignal.await(); } catch(InterruptedException ie) { ie.printStackTrace(); }”
Posted 21 Dec 2011 at 6:04 am ¶Thank you! That explains the reason why spinlock was ridiculously fast.
Posted 21 Dec 2011 at 7:21 am ¶Post a Comment