Java multithreading/concurrency 21, using Condition s to achieve blocking queues

Keywords: Java

The example on Java documents uses Condition s to implement blocking queues.
Suppose there is a fixed size buffer (the buffer is a queue, FIFO compliant), which supports storage and retrieval methods. If the buffer is empty and attempts to fetch data, the thread will block until the buffer has one available data; if the buffer is full, then attempts to write data, then the thread will block until there is available space. We want to have two separate waiting sets (Condition s blocking queues), a waiting thread for storing data and a waiting thread for fetching data, so that when the buffer is free, we can wake up a thread in the thread waiting set for storing data; when the buffer is from nothing to a few. When data is stored, a thread can be waked up in the "thread waiting set" to fetch data. This relationship can't be mistaken!
Conditions are created by Lock, and each Lock lock can create multiple Conditions objects. Only Conditions objects with the same lock can interact with each other.

public class ConditionDemo {

    static class BoundedBuffer {
        final Lock lock = new ReentrantLock();
        final Condition notFull = lock.newCondition();// Thread Waiting Set for Storing Data -- Waiting Because It's Full
        final Condition notEmpty = lock.newCondition();// Threads that fetch data wait for sets -- waiting because they are empty
        /* Implementing Buffer Queue (FIFO) with Array */
        final Object[] items = new Object[5];
        /*
         * putptr:Index Location of Stored Data
         * takptr:Index location of fetch data 
         * count:Current queue size
         */
        int putptr, takeptr, count;

        public void put(Object x) throws InterruptedException {
            lock.lock();/* Lock code that needs synchronization */
            try {
                /*If the array is full, the current thread enters the waiting set of stored data. Don't use if for fear of thread fake Awakening*/
                while (count == items.length) {
                    System.err.println(Thread.currentThread().getName()+"Write data["+putptr+"]abnormal:Buffer pool full");
                    notFull.await();
                }
                /* Data Writing */
                System.out.println(Thread.currentThread().getName()+"Write data["+putptr+"]:"+String.valueOf(x));
                items[putptr] = x;
                /* Change the write index position + 1 (in preparation for the next write) and reset to the start of the array if it exceeds the array boundary */
                if (++putptr == items.length)
                    putptr = 0;
                /* Increase the count of effective elements in the array, i.e. queue size plus 1 */
                ++count;
                /* At this point, there must be data available in the queue, waking up threads from the "thread waiting set for fetching data" */
                notEmpty.signal();
            } finally {
                /* lock It must be released in the final block. Otherwise, if the protected code throws an exception, the lock may never be released. */
                lock.unlock();
            }
        }

        public Object take() throws InterruptedException {
            lock.lock();/* Lock code that needs synchronization */
            try {
                /*If the array is empty, the current thread enters the waiting set for reading data. Don't use if for fear of thread fake Awakening*/
                System.err.println("Thread preparation");
                while (count == 0){
                    System.err.println(Thread.currentThread().getName()+"Read data["+putptr+"]abnormal:Buffer pool empty");
                    notEmpty.await();
                }
                System.err.println("Threads start reading data");
                /*Read data*/
                Object x = items[takeptr];
                System.out.println(Thread.currentThread().getName()+"Get queue data["+takeptr+"]:"+x);
                /* Change the read index position + 1 (in preparation for the next read) and reset to the starting point of the array if it exceeds the array boundary */
                if (++takeptr == items.length)
                    takeptr = 0;
                /* Reduce the number of effective elements in an array, i.e. reduce the queue size by 1 */
                --count;
                /* At this point, there must be space available in the queue to wake up threads from the "thread waiting set for storing data" */

                notFull.signal();
                return x;
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final BoundedBuffer boundbuffe =new BoundedBuffer();

        for(int i=0;i<10;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(new Random().nextInt(3000));
                        int temp=new Random().nextInt(1000);
                        boundbuffe.put(temp);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        for(int i=0;i<5;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(new Random().nextInt(500));
                        boundbuffe.take();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    }

}

The example is not difficult to understand. I commented it in Chinese. By adjusting the number of threads in memory and fetch and sleep time, it is easy to create "write data exception"
Running results (partial screenshots):

Think of a problem. The await() method of condition lets a thread wait. When the thread wakes up, does it go on executing? Or refresh, re-execute the whole method or lock block?
To verify this, the notEmpty.await() code block is surrounded by two outputs in the take() method, which becomes:

/*If the array is empty, the current thread enters the waiting set for reading data without using if for fear of thread spoofing.*/
System.out.println(Thread.currentThread().getName()+"Thread preparation");
while (count == 0){
    System.err.println(Thread.currentThread().getName()+"Read data["+putptr+"]abnormal:Buffer pool empty");
    notEmpty.await();
}
System.out.println(Thread.currentThread().getName()+"Threads start reading data");

Rerun result:

The phrase "thread preparation" appears only once, indicating that if the thread await() is awakened by other threads, it will continue to run down.

Posted by Jimmy79 on Wed, 03 Jul 2019 12:08:52 -0700