Master Series of Concurrent Programming - 4. Thread Communication

Keywords: Java Programming

Master high concurrency, high availability architecture

Lesson 2 Concurrent Programming

Learn concurrent programming from this lesson. This paper mainly introduces the basic knowledge of concurrent programming, lock, memory model, thread pool and the use of various concurrent containers.

Section 4 Thread Communication

Concurrent Programming Thread Communication AQS Condition Lock

This section learns about communication between threads and handwritten cache queues.

Implementation of Thread Communication

There are two kinds:

  1. Keywords synchronized with wait(), notify(), notify all () to achieve
  2. Using Lock and Condition s

This section focuses on Condition.

Condition

It's an interface. The implementation class is ConditionObject, an internal class of AQS

public interface Condition {
    void await() throws InterruptedException;
    void awaitUnInterruptibly();
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal();
    void signalAll();
}
  • await() causes the current thread to wait and release the lock; when other threads execute signal() or signalAll(), the thread retrieves the lock and continues to execute; or when the thread is interrupted, it causes the thread to jump out of the wait. This method is similar to Object.wait().
  • awaitUnInterruptibly(), similar to await, does not respond to interruptions, even in a waiting state
  • signal(), used to wake up a waiting thread. The relative signalAll() method wakes up all waiting threads. Similar to Object.notify()

condition.await() must be used between lock and unlock

Use lock. new Condition () to get the Condition

False Waiting and False Awakening

When await() or signal() is executed, the thread does not necessarily respond immediately, and false waiting and false wake-up occur. This is a concession to the semantics of the underlying platform. If you use "if (! Condition)" to make judgments, there will be problems, so generally use "while(! Condition)" to prevent this situation.

No IF, WHILE

if (!condition) {
    condition.await();
}
while (!condition) {
    condition.await();
}

Implementation of Buffer Queue

Upper Code (Producer-Consumer Model)

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description: Buffer queue
 * @Author: lsw
 * @Version: 1.0
 */
public class BoundedBuffer {

    final Lock lock = new ReentrantLock(); // Lock object

    final Condition notFull = lock.newCondition(); // Writing Conditions
    final Condition notEmpty = lock.newCondition(); // Reading Conditions

    final Object[] items = new Object[100]; // container

    int putIdx, // Write an index
        takeIdx, // Reading Index
        count; // Current Quantity

    public void put(Object it) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await(); // When the container is full, make the write thread wait
            }

            // Normal Conditions
            items[putIdx] = it;
            putIdx++;

            // Save it to the end and start again.
            if (putIdx == items.length) {
                putIdx = 0;
            }
            count++;

            // Save in the object and read on the notification reader thread
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await(); // When the container is empty, the reader thread waits
            }

            // Normal Conditions
            Object it = items[takeIdx];
            takeIdx++;

            // If you read to the end, start from scratch
            if (takeIdx == items.length) {
                takeIdx = 0;
            }
            count--;

            // Wake-up Write Thread
            notFull.signal();

            return it;
        } finally {
            lock.unlock();
        }

    }

}

By creating multiple conditions for the same Lock, you can very flexibly control the execution or waiting of each thread. That's the strength of Condition.

LockSupport Tool Class

LockSupport.part() or LockSupport.unpark() will be used at the bottom when Lock is used to implement lock and unlock and Condition s are used to perform state operations on threads. Now let's look at this tool class.

public class LockSupport {
    static void park() {}
    static void park(Object blocker) {}
    static void parkNanos(long nanos) {}
    static void parkNanos(Object blocker, long nanos) {}
    static void parkUntil(long deadline) {}
    static void park(Object blocker, long deadline) {}
    static void unpark(Thread t) {}
}

The function of the park() method is to cause the current thread to enter a waiting WAITING queue until unpark() is called or the response interrupts.

The parkNanos() method is to cause the current thread to enter the waiting queue, and the waiting time should not exceed the specified time.

The parkUntil() method is to cause the current thread to enter a waiting queue until it exits at a deadline.

The parameter blocker is an object that can be used to record the waiting of threads to facilitate problem checking.

unpark() is used to wake up a specified thread

Underneath these functions are UNSAFE.park() and UNSAFE.unpark() of the invoked Unsafe local class library.

Posted by wzcocoon on Sun, 11 Aug 2019 20:19:26 -0700