DelayQueue源码学习

DelayQueue源码学习


DelayQueue是什么

​ DelayQueue实现了BlockingQueue,是一种无界队列,用来存放实现了Delayed的对象,不能存放null值,它是线程安全的。

​ DelayQueue是一种有序队列,对象存放顺序按照该队列存放对象的compareTo方法确定。

成员变量和构造方法

成员变量
private final transient ReentrantLock lock = new ReentrantLock();
private final Condition available = lock.newCondition();
private final PriorityQueue<E> q = new PriorityQueue<E>();
private Thread leader = null;

​ lock变量是可重入锁,用来保证DelayQueue的线程安全。

​ available是Condition对象,用来与lock进行配合,完成线程阻塞唤醒等工作。作用等同wait(),notify()。

​ p是一个优先级队列对象,该队列中实际存放对象的容器, 对DelayQueue的添加提取操作的实际操作对象。

​ leader是当前对该队列进行占用的线程(阻塞中),线程占用的时候会将此字段设置为正在占用的线程, 线程占用完毕将此字段置为null

构造方法
public DelayQueue() {}
public DelayQueue(Collection<? extends E> c) {
        this.addAll(c);
    }

有参构造调用父类的addAll()方法,将集合中的元素存入队列中。

主要方法

入队列 add() offer() put()
public boolean add(E e) {
        return offer(e);
    }
public void put(E e) {
        offer(e);
    }
public boolean offer(E e, long timeout, TimeUnit unit) {
        return offer(e);
    }
 public boolean offer(E e) {
     	// 加锁线程安全
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // 存数据
            q.offer(e);
            // 如果存进去是第一个元素 唤醒等待线程
            if (q.peek() == e) {
                leader = null;
                available.signal();
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

add,put,方法最终调用的都是offer方法,所以这里只分析offer方法。

首先加锁保证线程安全,再调用 PriorityQueue 中的offer方法(如下代码)添加数据。

/**
* PriorityQueue 中的方法
*/
public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
    	// 扩容机制
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            // 没有数据直接添加
            queue[0] = e;
        else
            // size不为空时,进行排序
            siftUp(i, e);
        return true;
    }
	//排序方法
	private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            // 由于DelayQueue初始化comparator为null
            siftUpComparable(k, x);
    }

	// 通过队列中存储元素的compareTo方法进行比较 这里是任务的开始执行时间从早->晚排序
    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }
出队列 take() poll()
public E poll() {
    	// 加锁保证线程安全
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E first = q.peek();
            // 队列没有元素 或者第一个元素没到执行时间 返回null
            if (first == null || first.getDelay(NANOSECONDS) > 0)
                return null;
            else
                // 移除并返回第一个元素
                return q.poll();
        } finally {
            lock.unlock();
        }
    }

// 延迟执行poll方法 
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) {
                    if (nanos <= 0)
                        return null;
                    else
                        nanos = available.awaitNanos(nanos);
                } else {
                    long delay = first.getDelay(NANOSECONDS);
                    if (delay <= 0)
                        return q.poll();
                    if (nanos <= 0)
                        return null;
                    first = null; 
                    if (nanos < delay || leader != null)
                        nanos = available.awaitNanos(nanos);
                    else {
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            long timeLeft = available.awaitNanos(delay);
                            nanos -= delay - timeLeft;
                        } finally {
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            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)
                    available.await();
                else {
                    long delay = first.getDelay(NANOSECONDS);
                    // 到了执行时间
                    if (delay <= 0)
                        return q.poll();
                    first = null; 
                    if (leader != null)
                        // 有线程正在占用 阻塞
                        available.await();
                    else {
                        // 没有线程 将占用线程设置为当前线程
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            // 阻塞距执行时间相差的时间
                            available.awaitNanos(delay);
                        } finally {
                            // 占用线程如果是当前线程 将占用线程置空 
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            // 取出元素完毕 如果没有其他线程正在占用 并且 队列中还有元素 唤醒线程
            if (leader == null && q.peek() != null)
                available.signal();
            lock.unlock();
        }
    }

以上的poll方法和take方法都是移除并返回第一个元素,区别是take方法当队列为空时候会阻塞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值