DelayQueue

文章目录

      • 简介
      • 用法
      • 主要属性
      • 入队列
      • 出队列
      • 总结

简介

DelayQueue是一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的Delayed 元素。 DelayQueue中存储的所有元素必须实现Delayed接口 。

用法

public class Main {
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<DelayedTest> delayQueue = new DelayQueue<>();
        delayQueue.put(new DelayedTest(2000+System.currentTimeMillis()));
        delayQueue.put(new DelayedTest(4000+System.currentTimeMillis()));
        System.out.println(delayQueue.take().toString());
        System.out.println(delayQueue.take().toString());
    }
}

class DelayedTest implements Delayed {

    long deadline;

    public DelayedTest(long deadline) {
        this.deadline = deadline;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return deadline - System.currentTimeMillis();
    }

    @Override
    public int compareTo(Delayed o) {
        return (int) (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }

    @Override
    public String toString() {
        return "DelayedTest{" +
                "deadline=" + new Date(deadline).toString() +
                '}';
    }
}

主要属性

// 锁
private final transient ReentrantLock lock = new ReentrantLock();
// 优先级队列
private final PriorityQueue<E> q = new PriorityQueue<E>();
// 标记当前是否有线程在排队
private Thread leader = null;
// 条件队列,表示当前队列是否有元素可取
private final Condition available = lock.newCondition();

入队列

// 入队不会阻塞
public void put(E e) {
    offer(e);
}

public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    // 加锁
    lock.lock();
    try {
        // 入队列
        q.offer(e);
        if (q.peek() == e) {
            // 如果添加的元素刚好是堆顶,就把leader置为空
            leader = null;
            // 唤醒等待在条件available上的线程
            available.signal();
        }
        return true;
    } finally {
        // 释放锁
        lock.unlock();
    }
}

出队列

// 出队,若队列没有可出队元素,则不会阻塞
public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        E first = q.peek();
        if (first == null || first.getDelay(NANOSECONDS) > 0)
            return null;
        else
            return q.poll();
    } finally {
        lock.unlock();
    }
}

// 出队,若队列没有可出队元素,则阻塞
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    // 加锁
    lock.lockInterruptibly();
    try {
        for (;;) {
            // 获取堆顶元素
            E first = q.peek();
            // 若对顶元素为空,则需要等待
            if (first == null)
                available.await();
            else {
                // 获取堆顶元素到期时间
                long delay = first.getDelay(NANOSECONDS);
                // 到期时间小于0,说明已到期,可以出队
                if (delay <= 0)
                    return q.poll();
                // 若堆顶元素的到期时间大于0,则不能出队列,需要阻塞
                first = null; // don't retain ref while waiting
                if (leader != null)
                    available.await(); // 如果 leader 不为空,前面有线程已经在等待了,直接阻塞
                else {
                    // 获取当前线程
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {
                        // 等待 delay 后唤醒
                        available.awaitNanos(delay);
                    } finally {
                        // 如果此时的leader为当前线程,则需要将leader设置为null
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
        // 如果leader == null,说明没有线程在排队
        // q.peek() != null,说明队列还有元素
        if (leader == null && q.peek() != null)
            available.signal(); // 唤醒正在等待的线程
        lock.unlock(); // 释放锁
    }
}

总结

DelayQueue内部使用优先级队列来存储元素,通过使用ReentrantLock实现入队与出队的安全访问。

你可能感兴趣的:(java多线程)