Lassen Sie uns über die BlockingQueue-Implementierungsklasse java.util.concurrent.DelayQueue (JDK1.8-Quellcode-Analyse) sprechen.

DelayQueue-Quellcode-Analyse



Vorwort

öffentliche Klasse DelayQueue erweitert AbstractQueue
implementiert BlockingQueue {

AbstractQueue: Das Grundgerüst von Queue.
BlockingQueue: Blocking Queue-Schnittstelle.
Delayed: Delay-Schnittstelle, die die Methode getDelayed enthält, um die verbleibende Verzögerungszeit des Objekts zurückzugeben. Diese Schnittstelle implementiert Comparable


Tipp: Das Folgende ist der Inhalt dieses Artikels. Die folgenden Fälle dienen als Referenz

Erstens, DelayQueue-Quellcodeteil

1. Konstruktor

//无参空构造函数
public DelayQueue() {
    
    }

//带参构造函数
public DelayQueue(Collection<? extends E> c) {
    
    
    this.addAll(c);
}

//加入一个集合的元素到该队列中去
public boolean addAll(Collection<? extends E> c) {
    
    
    if (c == null) //不能加入空元素
        throw new NullPointerException(); //空指针异常
    if (c == this)
        throw new IllegalArgumentException(); //非法算术异常(不能自己加自己)
    boolean modified = false;
    for (E e : c) //遍历集合元素添加到队列中去
        if (add(e)) //调用了本类的offer方法,成功时返回true
            modified = true;
    return modified; //只有队列被修改并且没有抛出异常的情况下将会返回true
}

2. Mitgliedsvariablen

//该延迟队列底层是用的一个优先级队列来存储数据的
private final PriorityQueue<E> q = new PriorityQueue<E>();

//全局锁
private final transient ReentrantLock lock = new ReentrantLock();

//当队列头部有新元素可用或新线程可能需要成为leader时发出的条件。
private final Condition available = lock.newCondition();

///

//关于Condition中线程的挂起和唤醒的一些方法的使用
//Condition的创建
ReentrantLock lock = new ReentrantLock();  //首先需要一把锁
Condition condition = lock.newCondition();  //通过该锁实例对象创建Condition

//该方法阻塞当前线程直到被其他线程唤醒或者该线程被中断
condition.await();
//该方法阻塞当前线程直到被其他线程唤醒或者该线程被中断或者超过指定long时间(时间单位为纳秒)
condition.awaitNanos(long);
//唤醒阻塞线程中的一个
condition.signal();
//唤醒阻塞的所有线程
condition.signalAll();
//执行上面方法时必须先获得lock锁(lock.lock()),否则将抛出异常java.lang.IllegalMonitorStateException。

//当一个线程要从队列中取元素时,队首的元素可能剩余延迟时间>0此时,该线程需要
//等待剩余延迟时间然后才可以等待其他线程将其唤醒,此时其他线程也可从队列中获取元素,发现leader不为null的时候,其他线程将会直接阻塞等待唤醒
private Thread leader = null;

3. Hauptmethode

Tritt dem Team bei

Angebotsmethode

public boolean offer(E e) {
    
    
    final ReentrantLock lock = this.lock; //获取全局锁
    lock.lock(); //加锁
    try {
    
    
        q.offer(e); //向优先级队列加入元素e
        //如果加入的元素是队列中延迟时间最小的(优先级最高的)该元素则位于队首
        if (q.peek() == e) {
    
    
            leader = null; //leader线程所指向线程将于其他线程有相等的竞争
            available.signal(); //唤醒其他等待出队操作的线程(随机唤醒一个)
        }
        return true; //入队成功返回true
    } finally {
    
    
        lock.unlock(); //释放锁
    }
}

//该方法与上面方法相同
public boolean offer(E e, long timeout, TimeUnit unit) {
    
    
    return offer(e);
}

//该方法用于获得队首元素(加锁了)
public E peek() {
    
    
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
    
    
        return q.peek();
    } finally {
    
    
        lock.unlock();
    }
}

//与offer方法相同
public void put(E e) {
    
    
    offer(e);
}

Verlasse das Team

public E poll() {
    
    
    final ReentrantLock lock = this.lock; //全局锁
    lock.lock();  //加锁
    try {
    
    
        E first = q.peek(); //获得优先级队列中的队首元素(不移除该元素)
        if (first == null || first.getDelay(NANOSECONDS) > 0)
            return null;//队列为null,或有剩余延迟时间,直接返回空
        else
            return q.poll(); //出队
    } finally {
    
    
        lock.unlock(); //释放锁
    }
}

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    
    
    long nanos = unit.toNanos(timeout); //超时时间
    final ReentrantLock lock = this.lock; //获取全局锁
    lock.lockInterruptibly(); //加锁线程中断时该锁可被打破释放
    try {
    
    
        for (;;) {
    
    
            E first = q.peek(); //返回队首元素
            if (first == null) {
    
     //队首为null则队列为空
                if (nanos <= 0) //等待时间<=0直接返回null
                    return null;
                else  //否则该线程阻塞nanos时间后自动被唤醒或中途被其他线程唤醒
                    nanos = available.awaitNanos(nanos);
            } else {
    
     //队列非空的情况
                long delay = first.getDelay(NANOSECONDS); //获取队首元素剩余延迟时间
                if (delay <= 0) //剩余延迟时间<= 0
                    return q.poll(); //直接出队poll过程 //a
                if (nanos <= 0) //剩余延迟时间>0并且超时时间 <= 0的情况下(该线程不等待)
                    return null; //直接返回null  //b
                first = null; // don't retain ref while waiting
                //1.这里当超时时间小于延迟时间时,该线程仍然等待nanos时间,因
				//为此时他释放了自己锁持有的锁资源,其他线程可以进行入队操作,
				//且队首元素可能被改变
				//2.如果当超时时间大于等于延迟时间时,并且leader不为null(说明
				//也有线程在进行出队操作),将会等待nanos时间
                if (nanos < delay || leader != null)
                    nanos = available.awaitNanos(nanos);
                else {
    
     //3. 没有其他线程在进行入队操作时
                    Thread thisThread = Thread.currentThread(); //获取当前线程
                    leader = thisThread; //设置成员变量leader为当前线程
                    try {
    
    
                        long timeLeft = available.awaitNanos(delay); //等待指定延迟时间
						//这里等待指定延迟时间后为什么不直接去得队首元素
						//和上面原因类似,此时其他线程可能进行入队或出队操作改变了队首元素
                        nanos -= delay - timeLeft; //更新超时时间
                    } finally {
    
    
						//1.这里leader可能为空因为在当前线程阻塞的时候,其他入队操作的线程可能将leader赋值为null,leader为null说明 当前线程是被其他入队操作线程唤醒的
                        if (leader == thisThread)
                            leader = null; 
                    }
                }
            }
        }
    } finally {
    
    
//队列中还有元素,并且不存在leader线程了
        if (leader == null && q.peek() != null)
            available.signal(); //唤醒其他阻塞的在出队上的线程
        lock.unlock(); //释放锁
    }
}


public E take() throws InterruptedException {
    
    
    final ReentrantLock lock = this.lock; //全局锁
    lock.lockInterruptibly(); //可中断加锁
    try {
    
    
        for (;;) {
    
    
            E first = q.peek(); //获取队首元素不移除
            if (first == null) //队列为null
                available.await(); //阻塞当前线程等待被唤醒
            else {
    
     //队列中存在元素的情况
                long delay = first.getDelay(NANOSECONDS); //获取延迟时间
                if (delay <= 0) //元素已过期
                    return q.poll(); //直接返回该元素
                first = null; // don't retain ref while waiting
                if (leader != null) //队首元素的剩余延迟时间大于0,有其他线程也在出队操作
                    available.await(); //直接阻塞当前线程
                else {
    
     //队首为未过期元素并且当前没有其他线程出队阻塞
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread; //将当前线程设置为leader
                    try {
    
    
                        available.awaitNanos(delay); //阻塞队首延迟时间后被唤醒
                    } finally {
    
     
						//leader可能为null,因为其他线程的入队操作导致的
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
    
    
		//队列中还含有其他元素并且leader为null
        if (leader == null && q.peek() != null)
            available.signal(); //唤醒其他阻塞在出队操作上的线程
        lock.unlock(); //锁释放
    }
}

um zusammenzufassen

  1. DelayQueue ist eine verzögerte Warteschlange, und die Elemente in der Warteschlange müssen die Delayed-Schnittstelle implementieren.
  2. Die unterste Ebene der Warteschlange verwendet eine unbegrenzte Warteschlange mit vollständig binärer Heap-Priorität
  3. Die DelayQueue-Warteschlange implementiert BlockingQueue, wodurch die Blockierungsvorgänge der Warteschlange aus der Warteschlange entfernt werden können (Warteschlange aus der Warteschlange entfernen und die Operation unter Verwendung einer globalen Sperre eingeben)
    : Wenn die Warteschlange null ist, blockieren Sie den aktuellen Thread und warten Sie, bis andere Threads aktiviert werden Die Warteschlange ist nicht null. Beurteilen Sie, ob das Element am Kopf der Warteschlange abgelaufen ist. Es wird direkt nach Ablauf zurückgegeben. Wenn der Leader null ist, wird bestimmt, ob der Thread nur für die Zeit blockiert ist oder auf andere wartet Fäden zum Aufwachen. poll, wird direkt zurückgegeben, wenn sich ein abgelaufenes Element in der Warteschlange ohne Parameter befindet, andernfalls wird null zurückgegeben. Beim Aufnehmen von Parametern ist es ähnlich, mit der Ausnahme, dass eine Zeitüberschreitung (wie lange der Thread warten kann) hinzugefügt wird und null direkt zurückgegeben wird, wenn die Zeitüberschreitung überschritten wird.
    Treten Sie dem Team bei: bieten Sie an, setzen Sie (Angebot wird direkt aufgerufen), geben Sie true direkt zurück, wenn Sie erfolgreich dem Team beitreten.
  4. Die Blockierungswarteschlange enthält einen Leader-Thread für Mitgliedsvariablen. Wenn mehrere Threads take nehmen, muss nicht jeder Thread nach einer bestimmten Wartezeit automatisch aktiviert werden. Nur der erste take-Thread führt eine solche Verarbeitung durch, und alle nachfolgenden take-Threads sind erforderlich um festzustellen, ob der Leader leer ist, um festzustellen, ob der Thread direkt erwartet wird. (Das Spin-Urteil der awaitNonas-Methode ist ineffizient).
  5. DelayQueue verfügt über eine Bedingungsvariable, die das Aufwecken und Blockieren zwischen Threads behandelt. Um die zugehörige Methode verwenden zu können, muss zuerst die entsprechende Sperre erworben werden, und der Thread muss zuerst die relevante Sperre erhalten, wenn er wartet oder wartet. und wieder um Erwerb konkurrieren, wenn es wieder erwacht ist.

Verzögerte Schnittstelle

public interface Delayed extends Comparable<Delayed> {
    
    

    /**
     * Returns the remaining delay associated with this object, in the
     * given time unit.
     *
     * @param unit the time unit
     * @return the remaining delay; zero or negative values indicate
     * that the delay has already elapsed
     */
    long getDelay(TimeUnit unit);
}

Ich denke du magst

Origin blog.csdn.net/weixin_41237676/article/details/109105221
Empfohlen
Rangfolge