Java数据结构:队列(2)

1、双端队列(Deque)

deque 特殊之处在于添加和删除项是非限制性的。可以从任一端添加和删除数据。这种混合线性结构提供了单个数据结构中的栈和队列的所有能力。

2、循环队列

用数组来实现队列的时候,利用头尾指针避免了出队时数据的搬移,只是再在 tail==数组长度 时,会有数据搬移操作,但数据搬移操作还是会影响到性能。解决方案就是循环队列。

指定队列固定大小,通过头尾指针控制元素的增删。循环队列的关键是做好对空和对满的条件判断。

前面用数组+指针实现的单向队列中,队满的判断条件是 tail == n,队空的判断条件是 head == tail。

循环队列:队列为空的判断条件仍然是 head == tail
当队满时,(tail+1)%n=head

(tail 指向的位置实际上总是没有存储数据的)

public class CircularQueue {
    /**
     * 容器
     */
    private Object[] elementData;

    /**
     * 队列的默认大小
     */
    private static final int DEFAULT_SIZE = 10;

    /**
     * 队列中头指针
     */
    private int head;

    /**
     * 队列中尾指针
     */
    private int tail;

    /**
     * 默认构造器
     */
    public CircularQueue() {
        this(DEFAULT_SIZE);
    }


    public CircularQueue(int capacity) {
        if (capacity < 0) {
            throw new RuntimeException();
        }
        elementData = new String[capacity];
    }

   // 入队
    public boolean enqueue(Object data) {
        // 队列满了
        if ((tail + 1) % elementData.length == head) {
            return false;
        }
        elementData[tail] = data;
        tail = (tail + 1) % elementData.length;
        return true;
    }

    // 出队
    public Object dequeue() {
        // 如果 head == tail 表示队列为空
        if (head == tail) {
            return null;
        }
        Object data = elementData[head];
        head = (head + 1) % elementData.length;
        return data;
    }
}

3、阻塞队列

队列为空的时候,从队头取数据会被阻塞。因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。

public class BlockingQueue {

    private List queue = new LinkedList();

    private int size;

    Lock lock = new ReentrantLock();

    Condition condition = lock.newCondition();

    public BlockingQueue(int size) {
        this.size = size;
    }

    //入队列时,如果队列满了,则阻塞
    public void enqueue(Object item) throws InterruptedException {
        lock.lock();
        try {
            while (this.queue.size() == this.size) {
                condition.await();
            }
            if (this.queue.size() == 0) {
                condition.signalAll();
            }
            this.queue.add(item);
        } finally {
            lock.unlock();
        }

    }

    //出队列时,如果队列为空,则阻塞
    public Object dequeue() throws InterruptedException {
        lock.lock();
        try {
            while (this.queue.size() == 0) {
                condition.await();
            }
            if (this.queue.size() == this.size) {
                condition.signalAll();
            }
            return this.queue.remove(0);
        } finally {
            lock.unlock();
        }
    }
}

队列的应用:

队列在线程池中的应用:LinkedBlockingQueue(基于链表的阻塞队列)

构建可重用固定线程数的线程池:newFixedThreadPool,FixedThreadPool的corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。当线程池中的线程数大于corePoolSize时,keepAliveTime设置为0L,意味着多余 的空闲线程会被立即终止。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

执行流程:

1)如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务。

2)在线程池完成预热之后(当前运行的线程数等于corePoolSize),将任务加入LinkedBlockingQueue。

3)线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue获取任务来执行

使用无界队列作为工作队列,会对线程池带来如下影响:

  1. 当线程池中的线程数只要一达到corePoolSize后,新任务将添加到无界阻塞队列中。因此线程池中的线程数不会超过corePoolSize。
  2. 无界队列所以maximumPoolSize将是一个无效参数,那么keepAliveTime也是一个无效参数。
  3. 线程池不会有拒绝任务,来者不拒,均加入队列中
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值