Concurrency
Java Concurrency
The Producer-Consumer problem
Producer Consumer Full Explanation and Solution

The problem
There are two processes, a producer and a consumer, that share a common buffer with a limited size.
The producer “produces” data and stores it in the buffer, and the consumer “consumes” the data, removing it from the buffer.
Having two processes that run in parallel, we need to make sure that the producer will not put new data in the buffer when the buffer is full and the consumer won’t try to remove data from the buffer if the buffer is empty.
Concepts
Thread lifecycle:

Volatile: The Java volatile keyword is used to mark a Java variable as “being stored in main memory”.
class SharedObj
{
// volatile keyword here makes sure that
// the changes made in one thread are
// immediately reflected in other thread
static volatile int sharedVar = 6;
}
Synchronized:
A Java synchronized block marks a method or a block of code as synchronized. Java synchronized blocks can be used to avoid race conditions.
public synchronized void add(int value){
this.count += value;
}
Race Conditions:
A race condition is a special condition that may occur inside a critical section.
A critical section is a section of code that is executed by multiple threads and
where the sequence of execution for the threads makes a difference in the result
of the concurrent execution of the critical section.
Thread Class
public class CreatingStartingThreads {
// 1. extends from Thread class
public static class MyThread extends Thread {
// 2. Override and implement run method
@Override
public void run() {
System.out.println("Hello world Thread!");
}
public static void main(String[] args) {
// 3. start the thread!
Thread myThread = new MyThread();
myThread.start();
}
}
}
Runnable Interface
public class ImplementingRunnable {
// 1. implement runnable interface
public static class MyRunnable implements Runnable {
// 2. Override and implement run method
@Override
public void run() {
System.out.println("Hello world Runnable!");
}
public static void main(String[] args) {
// 3. start the thread but passing a new Runnable!
Thread myThread = new MyThread(new Runnable());
myThread.start();
}
}
}
Thread class vs Runnable interface
Choose runnable over thread!
- A new Runnable can be passed as a parameter in a thread, as well as a thread name.
- A Runnable can be passed to an executor, in other words its supported by Thread pools.
- Implementing an Interface is more flexible than Extending a class.
- Runnable can be implemented with Lambda expressions and anonymous classes.