Java Concurrency Notes I

  • Synchronized

static volatile Boolean running = false;

public static void launchTask() {
synchronized(running) {
if(running) return;
running = true;
}
//do something...
running = false;
}

Synchronized method

synchronized void incrementSync() {
count = count + 1;
}

Synchronized block

void incrementSync() {
synchronized (this) {
count = count + 1;
}
}

Internally Java uses a so called monitor also known as monitor lock or intrinsic lock in order to manage synchronization. This monitor is bound to an object, e.g. when using synchronized methods each method share the same monitor of the corresponding object.

All implicit monitors implement the reentrant characteristics. Reentrant means that locks are bound to the current thread. A thread can safely acquire the same lock multiple times without running into deadlocks (e.g. a synchronized method calls another synchronized method on the same object).

  • Lock

    private static boolean running = false;
private static Lock execLock = new ReentrantLock();

public void launchExpensiveTask() {
if (running) return; // fast exit without sync
if (!execLock.tryLock()) return; // quit if lock is not free
try {
running = true;
runExpensiveTask();
} finally {
running = false;
execLock.unlock();
}
}
  1. ReentrantLock
  2. ReadWriteLock
  3. StampedLock
  • Semaphore

In addition to locks the Concurrency API also supports counting semaphores. Whereas locks usually grant exclusive access to variables or resources, a semaphore is capable of maintaining whole sets of permits. This is useful in different scenarios where you have to limit the amount concurrent access to certain parts of your application.

private Semaphore semaphore = new Semaphore(1);

public void launchExpensiveTask() {
if (semaphore.tryAcquire()) {
try {
runExpensiveTask();
} finally {
semaphore.release();
}
}
}

Synchronized allows only one thread of execution to access the resource at the same time. Semaphore allows up to n (you get to choose n) threads of execution to access the resource at the same time.

  • Queue

private final int CPU_COUNT = 4;
private BlockingQueue<Runnable> lightTaskQueue = new LinkedBlockingDeque<Runnable>();
private ThreadPoolExecutor lightExecutor = new ThreadPoolExecutor(CPU_COUNT, CPU_COUNT, 60L, TimeUnit.SECONDS, lightTaskQueue);
private BlockingQueue<Runnable> heavyTaskQueue = new LinkedBlockingDeque<Runnable>();
private ThreadPoolExecutor heavyExecutor = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS, heavyTaskQueue);

Read more