Abstract
This article contains information about Concurrency applied to the world of Java in a compact format.
Concurrency basics
Parallelism in von-Neumann based machine architectures is actually just an emulated behaviour by process schedulers and needs further process synchronisation to work. As software engineer you usually have to coordinate thread calls of multiple threads to shared resources by hand to avoid logical (time-causal) issues and technical glitches.
But computer science and Java provides you an arsenal of algorithms, procedures and methods to handle these issues.
The classical way to secure critical resource accesses is an Instance Mutex (binary locking attribute for mutual exclusion) by applying a synchronized-block.
public class Dangerous { private final Object lock = new Object(); public void callMe() { synchronized (lock) { // do something critical here } } }
The locking can also be made on a method level by using a synchronized-method.
public class Dangerous { private final Object lock = new Object(); public synchronized void callMe() { // do something critical here } }
There is also another variant by using a Semaphore (Access-Counter Variable), that you can use if you need finer control about handling the number of threads. The concept of a Semaphore for process synchronisation was introduces by Edsger W. Djikstra in 1965.
A simple counting Semaphore class exists in Java since 1.5 and you can use the class like this:
public class Dangerous { // Construct an instance semaphore private final Semaphore sem = new Semaphore(1); public void callMe() { sem.acquire(); try { // do something critical } finally { sem.release(); } } }
Avoiding Deadlocks
Deadlocks can occur if a thread waits infinite (usually for the finishing of another thread), because the processing scheduler never gives a thread any processing time. The waiting thread actually waits and dies in starvation. A cascade effect occurs if other threads wait for the result of the starved thread. This leads to an overall blocking system or application.
Symptoms
- A hanging operating system or application
Causes
- Synchronized Methods or Synchronized Blocks may lead to thread starvation / deadlock if these were called in many concurrent threads with the same priority and without a straight process ordering policy in the (JVM) scheduler
Solution
Use a ReentrantLock or ReentrantReadWriteLock. Both classes provide you a “fair-scheduling” semantics, that actually gives higher priority to the longest waiting thread.
You should use a ReentrantLock with a try-finally like this:
public class Dangerous { /** * A lock with fair-scheduling policy. */ private final ReentrantLock lock = new ReentrantLock(true); public void callMe() { // Get a lock lock.lock(); try { // do something critical } finally { // Unlock lock.unlock(); } } }