Lock, Semaphore, and SpinLock in JDK 5

Tags:

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 responses to “Lock, Semaphore, and SpinLock in JDK 5”

  1. Invalid algo Avatar
    Invalid algo

    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
    for lock: 2201
    for semaphore: 2305
    for spinlock: 2304

  2. Invalid algo Avatar
    Invalid algo

    Sorry code did not get through

    “try { startSignal.await(); } catch(InterruptedException ie) { ie.printStackTrace(); }”

  3. Minkoo Avatar
    Minkoo

    Thank you! That explains the reason why spinlock was ridiculously fast.

Leave a Reply

Your email address will not be published. Required fields are marked *