[Reserved] Java programming logic (72) - Explicit Conditions

The section we introduce explicit locks This section describes the explicit conditions associated introduce its usage and principles. Explicit conditions may also be called a condition variable, queue conditions, or conditions, we may later be used interchangeably.

usage

Basic concepts and methods

Lock for race conditions to solve the problem, provided that the cooperation mechanism between threads. Explicit locks the synchronzied corresponds to the explicit conditions wait / notify correspond. wait / notify and synchronized with the use of the explicit conditions with the use of an explicit lock.

Conditions associated with the lock, created by an explicit condition variables need to lock, Lock interface defines the create method:

Condition newCondition();

Condition condition variable indicates, is an interface, which is defined as:

Copy the code
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();
}
Copy the code

the await () corresponding to the Object wait (), signal () corresponding to notify, signalAll () corresponding to notifyAll (), the semantics is the same.

Similar wait Object methods, there are several methods defined in the await the waiting time, but some more features:

Copy the code
// wait time is a relative time, since if the wait timeout returns, the return value is false, otherwise to true 
Boolean the await (Long Time, TimeUnit Unit) throws InterruptedException; 
// wait time is a relative time, but the parameter nanosecond units, return the actual value is nanosTimeout minus the time to wait 
Long awaitNanos (Long nanosTimeout) throws InterruptedException; 
// wait time is the absolute time, if due to waiting for a timeout returns, the return value is false, otherwise to true 
boolean awaitUntil (a Date DEADLINE) throws InterruptedException;
Copy the code

These methods are await response to an interrupt, if the interrupt occurs, throws InterruptedException, but interrupt flag will be cleared. Condition also defines a method that does not wait for response to interrupts:

void awaitUninterruptibly();

This method does not end due to the interruption, but when it returns, if the waiting process interrupt occurs, the interrupt flag will be set.

In general, the method Object and wait, like, need to acquire a lock before calling await method, if no locks, throws an exception IllegalMonitorStateException. After await After entering the queue, and will release the lock, release the CPU, when the other thread will wake it up, or wait times out, or after the occurrence of interrupt exception, it will need to re-acquire the lock acquiring a lock, will await method from drop out.

In addition, the method Object and wait, like, after await return, does not mean it will certainly wait for conditions to meet, want to await the call is usually placed inside a loop, only the conditions are met to exit.

In general, signal / signalAll and notify / notifyAll, like, call them need to obtain the lock, if no locks, throws an exception IllegalMonitorStateException. signal and notify the same, pick a thread to wake up, signalAll and notifyAll Like, wake up all waiting threads, but these threads need to re-awakened after competing lock, the lock will be acquired after returning from await call.

Usage Example

ReentrantLock realized newCondition method through which we look at the basic usage conditions. We achieve similar examples in Section 67 WaitThread, after a thread started before executing an action, wait for the main thread to give it instructions, after receiving instruction is executed, the sample code is:

Copy the code
public class WaitThread extends Thread {
    private volatile boolean fire = false;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    @Override
    public void run() {
        try {
            lock.lock();
            try {
                while (!fire) {
                    condition.await();
                }
            } finally {
                lock.unlock();
            }
            System.out.println("fired");
        } catch (InterruptedException e) {
            Thread.interrupted();
        }
    }

    public void fire() {
        lock.lock();
        try {
            this.fire = true;
            condition.signal();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        WaitThread waitThread = new WaitThread();
        waitThread.start();
        Thread.sleep(1000);
        System.out.println("fire");
        waitThread.fire();
    }
}
Copy the code

Of particular note, do not confuse the signal / signalAll and notify / notifyAll, notify / notifyAll method is defined in Object, Condition objects there, little attention will misuse, such as, for fire method in the above example, it may be It will be written as:

Copy the code
public void fire() {
    lock.lock();
    try {
        this.fire = true;
        condition.notify();
    } finally {
        lock.unlock();
    }
}
Copy the code

Written this way, the compiler does not complain, but throws IllegalMonitorStateException running, because the call is not synchronized within notify statement.

Also, avoid lock and synchronzied mix, so very confusing, such as:

Copy the code
public void fire() {
    synchronized(lock){
        this.fire = true;
        condition.signal();
    }
}
Copy the code

Remember, explicit conditions explicit lock fitting, wait / notify and synchronized with.

Producer / consumer model

In verse 67, we wait / notify realized the producer / consumer model, we mentioned a limitation wait / notify, it can only have a condition waiting queue, waiting for the analysis conditions are very complex. In the producer / consumer model, in fact, there are two conditions associated with a full queue, a queue related to empty. Using explicit lock condition can create multiple queue. Here, we use the explicit lock / re-implemented under conditions wherein blocking queue code:

Copy the code
static class MyBlockingQueue<E> {
    private Queue<E> queue = null;
    private int limit;
    private Lock lock = new ReentrantLock();
    private Condition notFull  = lock.newCondition();
    private Condition notEmpty = lock.newCondition();


    public MyBlockingQueue(int limit) {
        this.limit = limit;
        queue = new ArrayDeque<>(limit);
    }

    public void put(E e) throws InterruptedException {
        lock.lockInterruptibly();
        try{
            while (queue.size() == limit) {
                notFull.await();
            }
            queue.add(e);
            notEmpty.signal();    
        }finally{
            lock.unlock();
        }
    }

    public E take() throws InterruptedException {
        lock.lockInterruptibly();
        try{
            while (queue.isEmpty()) {
                notEmpty.await();
            }
            E e = queue.poll();
            notFull.signal();
            return e;    
        }finally{
            lock.unlock();
        }
    }
}
Copy the code

Wait defines two conditions: less than (notFull), not empty (notEmpty), put in the process, if the queue is full, then the notFull waiting, take in the process, if the queue is empty, waiting on notEmpty, put operation after notification notEmpty, take notice notFull after the operation.

In this way, the code is more legible, while avoiding unnecessary wake up and check and improve efficiency. Java and contracting in class ArrayBlockingQueue had adopted a similar manner.

The principle
ConditionObject
understand the concept and usage of explicit conditions, how we look at it is to achieve ReentrantLock, which newCondition () code is:

public Condition newCondition() {
    return sync.newCondition();
}

internal sync is ReentrantLock class object which newCondition () code:

final ConditionObject newCondition() {
    return new ConditionObject();
}

ConditionObject is an internal class defined AQS, see fancy understand AQS section. ConditionObject implementation is also more complex, we passed some of the key code to briefly discuss the implementation principle. Internal ConditionObject also has a queue, waiting queue represents a condition whose members declared:

// head node queue conditions 
Private firstWaiter the Node transient; 
// conditions queue tail node 
private transient Node lastWaiter;

Is a member of AQS ConditionObject internal class, it can access the data directly AQS, such as defined in AQS lock waiting queue.

We look at several methods to achieve, look at the await method.

await realization analysis

Here is the code await method, we explain the basic idea by adding comments.

Copy the code
Final void the await public () throws InterruptedException { 
    // if the first wait for the interrupt flag is set, a direct throw an exception 
    IF (Thread.interrupted ()) 
        the throw new new InterruptedException (); 
    // 1. Create a node for the current thread, join conditions queue 
    the Node Node addConditionWaiter = (); 
    // release the lock held by 2. 
    int = savedState fullyRelease (Node); 
    int interruptMode = 0; 
    // 3. give up the CPU, waits until it is interrupted or becomes isOnSyncQueue to true 
    / / isOnSyncQueue node is true to the other threads wait queue condition 
    // moved outside the lock queue, waiting conditions have been met 
    the while {(isOnSyncQueue (node)!) 
        LockSupport.park (the this); 
        IF ((interruptMode = checkInterruptWhileWaiting (Node)) = 0)! 
            BREAK; 
    } 
    // 4. reacquire lock
    IF (! acquireQueued (Node, savedState) && interruptMode = THROW_IE) 
        interruptMode = REINTERRUPT; 
    IF (node.nextWaiter = null!) // Clean up IF Canceled 
        unlinkCancelledWaiters (); 
    // 5. The interrupt processing, throwing an exception or interrupt settings flag 
    IF (interruptMode = 0!) 
        reportInterruptAfterWait (interruptMode); 
}
Copy the code

awaitNanos achieve analysis

awaitNanos await the realization is substantially similar, the main difference is the waiting time will be defined as follows:

Copy the code
Final Long awaitNanos public (Long nanosTimeout) throws InterruptedException { 
    IF (Thread.interrupted ()) 
        the throw new new InterruptedException (); 
    the Node Node addConditionWaiter = (); 
    int = savedState fullyRelease (Node); 
    Long lastTime to System.nanoTime = (); 
    int = 0 interruptMode; 
    the while {(isOnSyncQueue (node)!) 
        IF (nanosTimeout <= 0L) { 
            // wait timeout, the node from the queue to the waiting condition of the external lock waiting queue 
            transferAfterCancelledWait (node); 
            BREAK; 
        } 
        // define maximum time 
        LockSupport.parkNanos (the this, nanosTimeout); 
        IF (! (interruptMode = checkInterruptWhileWaiting (the Node)) = 0) 
            BREAK;

        long now = System.nanoTime();
        //计算下次等待的最长时间
        nanosTimeout -= now - lastTime;
        lastTime = now;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null)
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
    return nanosTimeout - (System.nanoTime() - lastTime);
}
Copy the code

signal analysis to achieve

code signal method:

Copy the code
Final void Signal public () { 
    // verify the current thread holds the lock 
    IF (isHeldExclusively ()!) 
        the throw new new IllegalMonitorStateException (); 
    // wake-up call doSignal first thread in the waiting queue 
    the Node First = firstWaiter; 
    IF (= First! null) 
        doSignal (First); 
}
Copy the code

doSignal code not listed, which are basic logic:

  1. The wait queue to the node from the lock condition waiting queue
  2. Wake-up call LockSupport.unpark thread

summary

This section describes the explicit conditions of usage and implementation principle. It is used in conjunction with an explicit lock, compared with the wait / notify, conditions may support multiple queues, the code is more readable, more efficient, careful not to signal / signalAll mistakenly written as notify / notifyAll use.

From Day 70 to this section, we introduce the basics of Java and contract - the atomic variables and CAS, explicit lock and condition, based on these, Java and contract also offers a lot more easy to use high-level data structures, tools and services, from the beginning of the next section, let's explore some concurrent data structures.

Guess you like

Origin www.cnblogs.com/ivy-xu/p/12364391.html