阻塞队列之PriorityBlockingQueue

PriorityBlockingQueue

特点:

1、可以指定内部元素的排序规则(即出队规则),即实现该接口的对象:java.util.Comparator<T>;

2、内部数据结构是对象数据组,Object[] queue;

3、默认长度:11,最大长度:Integer.MAX_VALUE - 8;

4、不支持放入空值,会抛异常:NullPointerException

5、放入元素有要求,二选一:

     A、元素对象不用实现:java.lang.Comparable<T>,但是需要在构造参数中指定比较器:java.util.Comparator<T>

     B、元素对象实现:java.lang.Comparable<T>,构造无需指定比较器:java.util.Comparator<T>

     必须二选一,否则会抛异常,如果两个条件都满足了,那么默认会优先按指定的比较器来比较排序;

构造:

    //1、无惨构造,使用默认长度11,不指定比较器,要求元素必须实现Comparable接口
    public PriorityBlockingQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

    //2、指定容量的构造,不指定比较器
    public PriorityBlockingQueue(int initialCapacity) {
        this(initialCapacity, null);
    } 

    //3、指定容量和比较器的构造,元素顺序按照比较器指定的比较规则来
    public PriorityBlockingQueue(int initialCapacity,
                                 Comparator<? super E> comparator) {
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.lock = new ReentrantLock();
        this.notEmpty = lock.newCondition();
        this.comparator = comparator;
        this.queue = new Object[initialCapacity];
    }

    //4、通过指定集合构造队列
    public PriorityBlockingQueue(Collection<? extends E> c) {
        this.lock = new ReentrantLock();
        this.notEmpty = lock.newCondition();
        boolean heapify = true; // true if not known to be in heap order
        boolean screen = true;  // true if must screen for nulls
        if (c instanceof SortedSet<?>) {
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
            heapify = false;
        }
        else if (c instanceof PriorityBlockingQueue<?>) {
            PriorityBlockingQueue<? extends E> pq =
                (PriorityBlockingQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();
            screen = false;
            if (pq.getClass() == PriorityBlockingQueue.class) // exact match
                heapify = false;
        }
        Object[] a = c.toArray();
        int n = a.length;
        // If c.toArray incorrectly doesn't return Object[], copy it.
        if (a.getClass() != Object[].class)
            a = Arrays.copyOf(a, n, Object[].class);
        if (screen && (n == 1 || this.comparator != null)) {
            for (int i = 0; i < n; ++i)
                if (a[i] == null)
                    throw new NullPointerException();
        }
        this.queue = a;
        this.size = n;
        if (heapify)
            heapify();
    }

方法:

其方法和ArrayBlockingQueue和LinkedBlockingQueue类似,入队:add、offer、put;出队:remove、poll、take

其要注意的是:

1、入队时,元素会按照构造队列时指定的Comparator对象排序,或者,按照元素自身实现Comparable接口后的比较规则排序;

2、如果两种都满足,默认构造参数指定的Comparator有限;

应用场景:

1、放入队列的元素需要按照指定的规则排序出队,比如:带有不同优先级的任务,VIP客户的任务需要插队先处理;

使用示例:

import java.util.Comparator;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * PriorityQueue <br>
 * 特点:<br>
 * 可以定义排序规则(即出队规则)的阻塞队列(针对已经在队列内的元素)<br>
 * 数据结构:对象数组 <br>
 * 默认长度11 <br>
 * 不能放入空值 <br>
 * 放入元素有要求,二选一:
 * 1、元素对象不用实现:java.lang.Comparable<T>,但是需要在构造参数中指定比较器:java.util.Comparator<T>
 * 2、元素对象实现:java.lang.Comparable<T>,构造无需指定比较器:java.util.Comparator<T>
 * 必须二选一,否则会抛异常,如果两个条件都满足了,那么默认会优先按指定的比较器来比较排序;
 * 
 */
public class PriorityBlockingQueueTest {

	private PriorityBlockingQueue<CustTask> priorityBlockingQueue = new PriorityBlockingQueue<CustTask>(10,
			new Comparator<CustTask>() {
				public int compare(CustTask o1, CustTask o2) {
					return o1.getPriority() - o2.getPriority();// 优先级小的靠前排
				}
			});

	//注意:采用这种不指定比较器的构造,要求放入的元素必须实现Comparable接口,否则会抛异常;
	private PriorityBlockingQueue<CustTask> priorityBlockingQueue2 = new PriorityBlockingQueue<CustTask>();

	public PriorityBlockingQueueTest() {
	}

	class CustTask {
		private String name;
		private int priority;

		public CustTask(String name, int priority) {
			this.name = name;
			this.priority = priority;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public int getPriority() {
			return priority;
		}

		public void setPriority(int priority) {
			this.priority = priority;
		}

		@Override
		public String toString() {
			return "name:" + this.getName() + " , priority:" + this.getPriority();
		}
	}

	private void test() {
		for (int i = 0; i < 10; i++) {
			priorityBlockingQueue.offer(new CustTask("name" + i, 20 - i));
		}
		CustTask custTask = null;
		while (true) {
			custTask = priorityBlockingQueue.poll();// 为null表示没有元素了,不阻塞
			if (null != custTask) {
				System.out.println(custTask.toString());
			} else {
				break;
			}
		}
		System.out.println("over");
	}

	public static void main(String[] args) {
		new PriorityBlockingQueueTest().test();
	}
}

 

PriorityBlockingQueue是一个支持优先级排序的无界阻塞队列,下面从多个方面进行详细介绍: ### 基本性质 虽然被称为无界队列,但实际上它也是有界的,其最大长度为 `Integer.MAX_VALUE - 8`,这里减去 8 是因为基于数组实现,不同 JVM 对数组处理有所不同,这样做是为了避免 OOM [^3]。 ### 线程安全特性 它跟 `PriorityQueue` 最大的区别在于它是线程安全的。在入队和出队操作时使用同一把锁,在扩容时先解锁,再使用 CAS 原子操作,之后重新获取锁 [^1]。 ### 内部锁机制 类似于 `ArrayBlockingQueue`,`PriorityBlockingQueue` 内部使用一个独占锁来控制同时只有一个线程可以进行入队和出队操作。不过,它只使用了一个 `notEmpty` 条件变量,而没有 `notFull` 条件变量。这是因为它是无界队列,在执行 `put` 操作时永远不会处于 `await` 状态,所以也不需要被唤醒 [^2]。 ### 排序规则 `PriorityBlockingQueue` 是一个基于数组实现二叉堆(小顶堆,子节点大于父节点)的优先级队列,不满足队列先进先出的特性,而是会通过 `Comparable` 进行排序。要么在初始化时自定义 `Comparable`,要么存放的对象实现 `Comparable` 接口,当两者都有时,自定义的优先 [^3]。 ### 代码示例 以下是一个简单的 `PriorityBlockingQueue` 使用示例: ```java import java.util.concurrent.PriorityBlockingQueue; public class PriorityBlockingQueueExample { public static void main(String[] args) { // 创建一个 PriorityBlockingQueue PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>(); // 插入元素 queue.offer(3); queue.offer(1); queue.offer(2); // 取出元素 while (!queue.isEmpty()) { System.out.println(queue.poll()); } } } ``` ### 复杂度分析 在一堆数据中找出最大或最小元素,普通遍历的时间复杂度为 O(N),而 `PriorityQueue`(`PriorityBlockingQueue` 与之类似)实现了优化,能以接近 O(1) 的时间复杂度获取到最大或最小元素 [^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值