import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; class processor { // list to store number; + upper and lower limit for number of elements private List list = new ArrayList<>(); private final int MAX_Limit = 10; private final int MIN_Limit = 0; // lock with corresponding conditions private final ReentrantLock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); // value that is added to the list private int value = 0 ; // method to fill the list public void producer() throws InterruptedException { while(true) { // work forever lock.lock(); // try to aquire lock try { while (list.size() >= MAX_Limit){ // If the maximal size of the list is reached, System.out.println("Waiting for consumer to consume the numbers ...."); notFull.await(); // then wait } System.out.println("Adding the number " + value + " to list with " + list.size() + " elements."); list.add(value); // add number to list value++; notEmpty.signal(); // Signal that the list is not empty } finally { lock.unlock(); // unlock after finishing work } Thread.sleep((long) (Math.random() * 500)); // sleep random time } } // method to delete elements from the list public void consumer() throws InterruptedException { while(true) { // work forever lock.lock(); // try to aquire lock try { while (list.size() <= MIN_Limit){ // If the minimal size of the list is reached, System.out.println("Waiting for producer to produce the numbers ...."); notEmpty.await(); // then wait } int n = list.remove(list.size()-1); // remove element from list System.out.println("Removing the number " + n + " from list with " + (int) (list.size()+1) + " elements."); notFull.signal(); // Signal that the list is not full } finally { lock.unlock(); // unlock after finishing work } Thread.sleep((long) (Math.random() * 500)); } } } public class PCLocks { public static void main(String[] args) { processor process = new processor(); // create producer thread Thread T1 = new Thread(new Runnable() { @Override public void run() { try{ process.producer(); } catch (InterruptedException e) { e.printStackTrace(); } } }); // produce consumer thread Thread T2 = new Thread(new Runnable() { @Override public void run() { try{ process.consumer(); } catch (InterruptedException e) { e.printStackTrace(); } } }); // start both threads T1.start(); T2.start(); // wait for threads to finish (will not happen...) try { T1.join(); T2.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }