Locks guards a shared resource. Conditions provide a means for one thread to suspend execution (“wait”) until notified by another thread that some state condition may now be true.

General behavior is as follows:

After a Lock is created, new Condition instances are created to specify different conditions of an application. One thread holding the lock checks application state (a variable value or method result). It can then either call wait method on a Condition or call signal method that Condition has changed.

Let’s say, we have a queue and we have following requirements:

  • No item cannot be put into the queue, if it is full. Thread should wait, until queue is not full.
  • No item cannot be retrieved from the queue, if it is empty. Thread should wait until queue is not empty.

We will create a ReentrantLock instance and create 2 conditions from it: notFull and notEmpty. If a thread want to take an item when queue is empty, it will wait on notEmpty and will release the lock. When another thread gets the lock and puts an item to the queue, it will signal on notEmpty, notifying waiting threads to resume their operation. A similar flow is also valid for notFull condition.

public class LockConditionTest {

    public static void main(String[] args) throws InterruptedException {
        newCondition();
    }

    public static void newCondition() throws InterruptedException {
        final BlockingQueue blockingQueue = new BlockingQueue(2);
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        class Putter implements Runnable {

            @Override
            public void run() {
                try {
                    blockingQueue.put(new Object());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        class Taker implements Runnable {

            @Override
            public void run() {
                try {
                    blockingQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        threadPool.execute(new Putter());
        threadPool.execute(new Putter());
        threadPool.execute(new Putter());
        threadPool.execute(new Putter());
        threadPool.execute(new Taker());
        threadPool.execute(new Taker());

        Thread.sleep(1000);
        threadPool.shutdown();
        threadPool.awaitTermination(10, TimeUnit.SECONDS);
    }

    /**
     * Taken from javadoc of Condition
     */
    private static class BlockingQueue {

        private final Object[] items;
        private final Lock lock = new ReentrantLock();
        private final Condition notFull = lock.newCondition();
        private final Condition notEmpty = lock.newCondition();
        private int putPointer;
        private int takePointer;
        private int count;

        public BlockingQueue(int capacity) {
            items = new Object[capacity];
        }

        public void put(Object value) throws InterruptedException {
            lock.lock();
            try {
                while (count == items.length) {
                    System.out.println("Full, waiting");
                    notFull.await();
                    System.out.println("Stop waiting");
                }

                items[putPointer++] = value;
                if (putPointer == items.length) {
                    putPointer = 0;
                }

                count++;
                notEmpty.signalAll();
            } finally {
                lock.unlock();
            }
        }

        public Object take() throws InterruptedException {
            lock.lock();
            try {
                while (count == 0) {
                    System.out.println("Empty, waiting");
                    notEmpty.await();
                    System.out.println("Stop waiting");
                }

                Object result = items[takePointer++];
                if (takePointer == items.length) {
                    takePointer = 0;
                }

                count--;
                notFull.signalAll();
                return result;
            } finally {
                lock.unlock();
            }
        }
    }
}

 

 

Leave a Reply

Close Menu