一个生产者可以等待消费者接收元素的阻塞队列。TransferQueue可能在消息传递应用程序中很有用,在这种情况下,生产者有时会(使用transfer方法)等待消费者调用take或poll来接收元素,而在其他时候,生产者会直接将元素入队(使用put方法)而无需等待接收。TransferQueue还提供了非阻塞和超时版本的tryTransfer。TransferQueue还可以查询是否有任何线程正在等待元素,这是对peek操作的反向类比。
与其他阻塞队列一样,TransferQueue可能有容量限制。如果有限制,尝试的传输操作最初可能会阻塞,等待可用空间,以及/或者后续的阻塞等待消费者接收。请注意,在容量为零的队列中,例如SynchronousQueue,put和transfer实际上是等价的。
import java.util.concurrent.TransferQueue;
import java.util.concurrent.LinkedTransferQueue;
/**
* TransferQueue是Java中的一个接口,它是并发包中的一部分,用于在生产者和消费者之间传递元素。
* TransferQueue是一个生产者可以等待消费者接收元素的阻塞队列
* TransferQueue提供了几种方法,包括transfer()、tryTransfer()和tryTransfer(long timeout, TimeUnit unit)。
*/
public class TransferQueueTest {
public static void main(String[] args) {
TransferQueue<Integer> queue = new LinkedTransferQueue<>();
// 生产者线程
new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
//如果已经有消费者在等待接收该元素(在take或有定时轮询的情况下),则立即转移指定的元素
// 否则等待,直到该元素被消费者接收
queue.transfer(i);
System.out.println("Produced: " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
while (true) {
Integer value = queue.take();
System.out.println("Consumed: " + value);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
//Consumed: 1
//Produced: 1
//Produced: 2
//Consumed: 2
//Consumed: 3
//Produced: 3
//Produced: 4
//Consumed: 4
//Consumed: 5
//Produced: 5
}
import java.util.concurrent.TransferQueue;
import java.util.concurrent.LinkedTransferQueue;
public class TransferQueueExample {
public static void main(String[] args) {
TransferQueue<Integer> queue = new LinkedTransferQueue<>();
// 生产者线程
new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
//TransferQueue的transfer方法可以将元素立即转移给一个等待中的消费者,如果可能的话。
// 更准确地说,如果存在一个消费者已经在等待接收元素(在take或定时轮询中),则立即转移指定的元素;
// 否则返回false,不会将元素入队
if (queue.tryTransfer(i)) {
System.out.println("Produced: " + i);
} else {
System.out.println("Failed to produce: " + i);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
while (true) {
Integer value = queue.take();
System.out.println("Consumed: " + value);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
//Failed to produce: 1
//Produced: 2
//Failed to produce: 3
//Failed to produce: 4
//Failed to produce: 5
//Consumed: 2
}
DelayQueue 是一个无界的 BlockingQueue,
用于放置实现了 Delayed 接口的对象,
存放在DelayQueue 队列中的对象只能在其到期时才能从队列中取走。
实现 Delayed 接口的实现类需要重写 getDelay 和 compareTo 方法。
当生产者线程调用 put 方法添加元素时,
会触发 Delayed 接口中的 compareTo 方法进行排序,
也就是队列中的元素的顺序是按到期时间排序的,而非进入队列的顺序。排在队列头部的元素最早到期,越往后到期时间越晚。
消费者线程查看队列头部的元素,然后调用元素的 getDelay 方法,如果此方法的返回值 ≤0,则消费者线程会从队列中取出此元素处理。
如果 getDealy 方法返回值大于0,则消费者线程 wait 返回的时间值后,再次从队列头部取出元素,此时元素到期可以取出。
也就是说会线程会按照设定的时间进行阻塞。
注意:不能将 null 元素放置到这种队列中。
import java.util.concurrent.*;
public class ThreadPoolDelayedTaskExecutor {
/**
* 使用延迟队列进行存储
*/
private final DelayQueue<DelayedTask> taskQueue;
private final ExecutorService taskExecutor;
private final ExecutorService taskRetriever;
public ThreadPoolDelayedTaskExecutor(int taskExecutorPoolSize, int taskRetrieverPoolSize) {
this.taskQueue = new DelayQueue<>();
this.taskExecutor = Executors.newFixedThreadPool(taskExecutorPoolSize);
this.taskRetriever = Executors.newFixedThreadPool(taskRetrieverPoolSize);
startTaskRetriever();
}
public void execute(Runnable task, long delay) {
taskQueue.offer(new DelayedTask(task, delay));
}
private void startTaskRetriever() {
taskRetriever.execute(() -> {
while (true) {
try {
Runnable task = taskQueue.take();
taskExecutor.execute(task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
}
private static class DelayedTask implements Runnable, Delayed {
private final Runnable task;
private final long startTime;
public DelayedTask(Runnable task, long delay) {
this.task = task;
this.startTime = System.currentTimeMillis() + delay;
}
@Override
public void run() {
task.run();
}
@Override
public long getDelay(TimeUnit unit) {
long delay = startTime - System.currentTimeMillis();
return unit.convert(delay, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
if (this.startTime < ((DelayedTask) o).startTime) {
return -1;
} else if (this.startTime > ((DelayedTask) o).startTime) {
return 1;
} else {
return 0;
}
}
}
public static void main(String[] args) {
ThreadPoolDelayedTaskExecutor delayedTaskExecutor=new ThreadPoolDelayedTaskExecutor(3,3);
System.out.println("开始...");
delayedTaskExecutor.execute(()-> System.out.println("我是小孩也是王"),2000);
delayedTaskExecutor.execute(()-> System.out.println("我是小孩也是王2"),3000);
delayedTaskExecutor.execute(()-> System.out.println("我是小孩也是3"),2000);
}
}
可以用DelayQueue保存缓存元素的有效期。使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。这样可以实现缓存的自动清理,有效避免因为缓存过多或者缓存过期而导致的系统压力
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class Cache {
private final DelayQueue<DelayedElement> queue;
public Cache() {
queue = new DelayQueue<>();
}
public void put(String key, String value, long expiryTime) {
DelayedElement element = new DelayedElement(key, value, expiryTime);
queue.put(element);
}
public String get(String key) throws InterruptedException {
//一旦能从DelayQueue中获取元素时,表示缓存有效期到了
DelayedElement element = queue.take(); // 阻塞直到获取到元素
if (element.getKey().equals(key)) {
return element.getValue();
} else {
throw new InterruptedException("Element not found");
}
}
private static class DelayedElement implements Delayed {
private final String key;
private final String value;
private final long expiryTime;
public DelayedElement(String key, String value, long expiryTime) {
this.key = key;
this.value = value;
this.expiryTime = expiryTime;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
@Override
public long getDelay(TimeUnit unit) {
long delay = expiryTime - System.currentTimeMillis();
return unit.convert(delay, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
if (this.expiryTime < ((DelayedElement) o).expiryTime) {
return -1;
} else if (this.expiryTime > ((DelayedElement) o).expiryTime) {
return 1;
} else {
return 0;
}
}
}
}
PriorityQueue是Java中的一个类,它实现了Queue接口。PriorityQueue用于处理元素的队列,这些元素根据其自然排序或者比较器比较的结果排序。PriorityQueue保证队列头部元素总是在最优先的元素。何为最优先?就是根据比较器比较出来的结果排序第一的即为最优先,你可以使用PriorityQueue来实现类似于堆的数据结构。
一个基于优先级堆的无边界优先队列。优先队列中的元素根据它们的自然顺序,或者根据在队列构造时提供的比较器进行排序,这取决于使用的构造函数。优先队列不允许有空元素。依赖于自然排序的优先队列也不允许插入不可比较的对象(这样做可能会导致ClassCastException)。
该队列的头部是按照指定排序的最小的元素。如果多个元素具有相同的最小值,头部将是这些元素之一-关系的打破是任意的。队列检索操作poll、remove、peek和element访问的是队列头部的元素。
优先级队列是无边界的,但是有一个内部容量制约用于存储队列中元素的数组的大小。它总是至少和队列大小一样大。当元素被添加到优先级队列时,其容量会自动增长。增长策略的细节没有具体规定。
这个类及其迭代器实现了Collection和Iterator接口的所有可选方法。在方法iterator()中提供的Iterator和在方法spliterator()中提供的Spliterator并不保证以任何特定的顺序遍历优先级队列的元素。如果需要有序遍历,可以考虑使用Arrays.sort(pq.toArray())。
注意,此实现不是同步的。如果多个线程中的任何一个修改了队列,就不应该同时访问PriorityQueue实例。相反,应该使用线程安全的java.util.concurrent.PriorityBlockingQueue类。
实现注意事项:对于enqueue和dequeue方法(offer、poll、remove()和add),此实现提供O(log n)时间复杂度;对于remove(Object)和contains(Object)方法,线性时间复杂度;对于检索方法(peek、element和size),常数时间复杂度。
泛型中指定的类要具有可比较性,即要实现Comparable接口,或者指定一个比较器
import java.util.Arrays;
import java.util.PriorityQueue;
/**
在这个示例中,我们创建了一个新的PriorityQueue,并添加了四个元素。
由于PriorityQueue默认使用自然排序,所以这些元素会按照从小到大的顺序排列。
我们打印了队列头部的元素,然后使用poll()方法依次打印了所有元素。
*/
public class PriorityQueueTest {
public static void main(String[] args) {
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(30);
pq.add(20);
pq.add(15);
pq.add(5);
System.out.println("队列头部元素:" + pq.peek());
// 在遍历的时候还是会按照先进先出的方式进行遍历
for (Integer item : pq) {
System.out.println(item);
}
//只有当被移除时,才会查找并移除队列的头部元素
while (!pq.isEmpty()) {
//pq.poll();查找并移除队列的头部元素
System.out.println(pq.poll());
//打印数组中的元素:
//说明每次移除后,PriorityQueue只会将队列中最优先的放在前面,其它的保持不变
//5
//[15, 30, 20]
//15
//[20, 30]
//20
//[30]
//30
//[]
System.out.println(Arrays.toString(pq.toArray(new Integer[pq.size()])));
}
System.out.println(pq.isEmpty());
}
import java.util.PriorityQueue;
/**
* 在这个示例中,我们创建了一个新的PriorityQueue,并使用了自定义的比较器,
* 使得元素按照从大到小的顺序排列。然后我们添加了三个元素,并打印了队列头部的元素,最后使用poll()方法依次打印了所有元素。
*/
public class PriorityQueueTest {
public static void main(String[] args) {
/* PriorityQueue pq = new PriorityQueue<>(new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});*/
//使用lambda表达式替换匿名内部类:倒序排优先级
PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> o2 - o1);
pq.add(10);
pq.add(20);
pq.add(15);
System.out.println("队列头部元素:" + pq.peek());
while (!pq.isEmpty()) {
System.out.println(pq.poll());
}
}
}
import java.util.PriorityQueue;
/**
* 在这个示例中,我们创建了一个名为Student的类,然后创建了一个新的PriorityQueue,用于处理Student对象。
* 我们使用了自定义的比较器,使得Student对象按照age从大到小的顺序排列。然后我们添加了三个Student对象,并打印了队列头部的元素
*/
class Student {
int id;
String name;
double age;
public Student(int id, String name, double cgpa) {
this.id = id;
this.name = name;
this.age = cgpa;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Student{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append(", age=").append(age);
sb.append('}');
return sb.toString();
}
}
public class PriorityQueueTest {
public static void main(String[] args) {
/* PriorityQueue pq = new PriorityQueue<>(new Comparator() {
@Override
public int compare(Student s1, Student s2) {
return Double.compare(s2.age, s1.age);
}
});*/
//使用lambda表达式替换匿名内部类:按照学生年龄从大到小排优先级
PriorityQueue<Student> pq = new PriorityQueue<>((s1, s2) -> Double.compare(s2.age, s1.age));
pq.add(new Student(1, "张三", 25));
pq.add(new Student(2, "李四", 19));
pq.add(new Student(3, "王五", 18));
pq.add(new Student(3, "赵六", 22));
System.out.println("队列头部元素:" + pq.peek().name);
System.out.println("------");
while (!pq.isEmpty()){
System.out.println(pq.poll());
}
//队列头部元素:张三
//------
//Student{id=1, name='张三', age=25.0}
//Student{id=3, name='赵六', age=22.0}
//Student{id=2, name='李四', age=19.0}
//Student{id=3, name='王五', age=18.0}
}
}
任务调度:
在任务调度中,我们需要按照一定的优先级来执行任务。例如,在一个多线程的程序中,我们可以使用PriorityQueue来存储任务,并按照任务的优先级进行排序。当需要执行任务时,只需要从PriorityQueue中取出优先级最高的任务,然后执行即可。
事件驱动:
在事件驱动的程序中,我们需要按照事件的优先级来处理事件。例如,在一个网络服务器中,我们可以使用PriorityQueue来存储网络请求,并按照请求的优先级进行排序。当有新的网络请求到来时,只需要将请求加入PriorityQueue中,然后按照优先级来处理即可。
ArrayBlockingQueue
:这是一个基于数组的有界阻塞队列。此队列按照 FIFO(先进先出)的原则对元素进行排序。
LinkedBlockingQueue
:这是一个基于链表的阻塞队列,通过构造函数是否有指定队列容量来判断是否有界。
此队列按照 FIFO(先进先出)的原则对元素进行排序。
PriorityBlockingQueue
:这是一个具有优先级的无界阻塞队列。此队列按照元素的自然顺序或者提供的Comparator进行排序。
DelayQueue
:这是一个支持延时获取元素的无界阻塞队列。只有在延迟期满时才能从队列中获取元素。
SynchronousQueue
:这是一个不存储元素的阻塞队列。每个插入操作必须等待一个相应的删除操作,反之亦然。
LinkedTransferQueue
:这是一个基于链表的无界阻塞队列,此队列按照 FIFO(先进先出)的原则对元素进行排序,同时还支持数据的转移操作。
ConcurrentLinkedQueue
:这是一个基于链表的无界非阻塞队列,此队列按照 FIFO(先进先出)的原则对元素进行排序。ArrayDeque
:这是一个基于数组的双端队列,具有FIFO(先进先出)的语义。LinkedList
:这是一个基于链表的双端队列,具有FIFO(先进先出)的语义。PriorityQueue
:这是一个具有优先级的无界队列。此队列按照元素的自然顺序或者提供的Comparator进行排序。阻塞队列中的阻塞意为等待,与非阻塞队列相比的话有如下的不同点:
入队:
非阻塞队列:当队列满的时候,放入新的元素时,数据丢失。
阻塞队列:当队列满的时候,放入新的元素时,线程阻塞等待,等待队列中有出队的元素,再继续运行放进去。
出队:
非阻塞队列:当队列没有元素的时候,取数据时得到的是null。
阻塞队列:当队列没有元素的时候,取数据时,线程阻塞等待,什么时候有元素入队了,才可以继续运行取出元素。