Synchronization
When two or more threads need access to a shared resource, they need some way to ensure that the resource will be used by only one thread at a time. The process by which this is achieved is called synchronization.
- Key to synchronization is the concept of the monitor.
- A monitor is an object that is used as a mutually exclusive lock.
- Only one thread can own a monitor at a given time.
- When a thread acquires a lock, it is said to have entered the monitor.
- All other threads attempting to enter the locked monitor will be suspended until the first thread exits the monitor.
- These other threads are said to be waiting for the monitor.
- A thread that owns a monitor can reenter the same monitor if it so desires.

Thread Communication using wait(), notify() and notifyAll()
To avoid polling, Java includes an elegant interprocess communication mechanism via the wait( ), notify( ), and notifyAll( ) methods. These methods are implemented as final methods in Object, so all classes have them. All three methods can be called only from within a synchronized context.
- wait( ) tells the calling thread to give up the monitor and go to sleep until some other thread enters the same monitor and calls notify( ) or notifyAll( ).
- notify( ) wakes up a thread that called wait( ) on the same object.
- notifyAll( ) wakes up all the threads that called wait( ) on the same object. One of the threads will be granted access.
Example: Stack Operation
import java.util.ArrayList;
public class SyncStack {
// define a stack of characters using list
ArrayList<Character> stack = new ArrayList<Character>();
// method to put item to stack
public synchronized void push(char c) {
stack.add(c);
this.notify();
}
// method to get item from stack
public synchronized char pop() {
char c;
// check if stack is empty
while (stack.size() == 0) {
try {
// is empty, then wait
this.wait();
} catch (InterruptedException ex) {
// ignore it
}
}
// if stack is not empty, then get the character
c = stack.remove(stack.size() - 1); // LIFO
return c;
}
}
public class Producer implements Runnable {
SyncStack stack;
int num;
static int counter = 1;
public Producer(SyncStack stack) {
this.stack = stack;
num = counter++;
}
@Override
public void run() {
char c;
for (int i = 0; i < 10; i++) {
// generate random character
c = (char) (Math.random() * 26 + 'A');
stack.push(c);
System.out.println("Producer" + num + ": " + c);
// simulate long process, put thread to sleep
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
// do nothing
}
}
}
}
public class Consumer implements Runnable {
SyncStack stack;
int num;
static int counter = 1;
public Consumer(SyncStack stack) {
this.stack = stack;
num = counter++;
}
@Override
public void run() {
char c;
for (int i = 0; i < 10; i++) {
c = stack.pop();
System.out.println("Consumer" + num + ": " + c);
// simulate long process, put thread to sleep
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
// do nothing
}
}
}
}
public class SyncTest {
public static void main(String[] args) {
// create the stack object
SyncStack stack = new SyncStack();
// create producers and consumers and run
// create an array of threads
Thread[] threads = { new Thread(new Producer(stack)), new Thread(new Producer(stack)),
new Thread(new Consumer(stack)), new Thread(new Consumer(stack)) };
// loop threads and start
for (Thread t : threads) {
t.start();
}
}
}
Locks and ReentrantLocks
- The ReentrantLock class implements the Lock interface and provides synchronization tomethods while accessing shared resources.
- The code which manipulates the shared resource is surrounded by calls to lock and unlockmethod.
- This gives a lock to the current working thread and blocks all other threads which aretrying to take a lock on the shared resource.
- As the name says, ReentrantLock allows threads to enter into the lock on a resource morethan once.
- When the thread first enters into the lock, a hold count is set to one.
- Before unlocking the thread can re-enter into lock again and every time hold count isincremented by one.
- For every unlocks request, hold count is decremented by one and when hold count is 0,the resource is unlocked.
Executors Framework
- The ExecutorService in Java provides a flexible and efficient framework for asynchronoustask execution.
- It abstracts away the complexities of managing threads manually and allows developers tofocus on the logic of their tasks.
- The ExecutorService interface is part of the java.util.concurrent package and represents anasynchronous task execution service.
- It extends the Executor interface, which defines a single method execute (Runnablecommand) for executing tasks.
Executors
- Executors is a utility class in Java that provides factory methods for creating andmanaging different types of ExecutorService instances.
- It simplifies the process of instantiating thread pools and allows developers to easilycreate and manage executor instances with various configurations.
- The Executors class provides several static factory methods for creating different types ofexecutor services:
- FixedThreadPool: Creates an ExecutorService with a fixed number of threads.
- CachedThreadPool: Creates an ExecutorService with an unbounded thread pool thatautomatically adjusts its size based on the workload.
- SingleThreadExecutor: Creates an ExecutorService with a single worker thread.
- ScheduledThreadPool: Creates an ExecutorService that can schedule tasks to run aftera specified delay or at regular intervals.
- newWorkStealingPool: Creates a work-stealing thread pool with the target parallelismlevel.
ExecutorServices
- Tasks can be submitted to an ExecutorService for execution.
- These tasks are typically instances of Runnable or Callable, representing units of work thatneed to be executed asynchronously.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorExample {
public static void main(String[] args) {
// create executor service with pool size of 5
ExecutorService executor = Executors.newFixedThreadPool(5);
// create job
MyRunnable job = new MyRunnable();
// assign job to executor to be executed
executor.submit(job);
executor.submit(job);
executor.submit(job);
executor.submit(job);
executor.submit(job);
executor.submit(job);
// stop the executor service
executor.shutdown();
}
}
Concurrent Collections
- Concurrency in Java refers to the execution of a couple of threads concurrently.
- When multiple threads access and modify shared records systems, troubles like racesituations and data corruption can occur.
- To cope with those troubles, Java offers a hard and fast of concurrent collections withinthe java.util.concurrent package deal.
- Examples:
- ConcurrentHashMap
- CopyOnWriteArrayList