阻塞队列学习

阻塞队列(BlockingQueue)是java.util.concurrent包中的一种并发容器,它支持线程安全的阻塞操作,即在队列为空时获取元素会阻塞线程,直到有元素可以获取;在队列满时插入元素也会阻塞,直到队列有空间为止。阻塞队列非常适合在多线程环境下用于实现生产者-消费者模型。

阻塞队列的特性

  1. 线程安全:阻塞队列在插入、删除等操作时内部使用了锁机制,保证多线程环境下的线程安全性。
  2. 阻塞操作
    • 如果队列为空,获取元素时会阻塞,直到有元素可获取。
    • 如果队列已满,插入元素时会阻塞,直到队列有空位。
  3. 多种操作方法
    • 抛异常方法:如add()remove()element()等,在操作失败时抛出异常。
    • 返回特殊值的方法:如offer()poll()peek()等,操作失败时返回特定值,如nullfalse
    • 阻塞方法:如put()take(),操作失败时阻塞线程直到成功。
    • 超时方法:如offer(e, time, unit)poll(time, unit),操作失败时等待一段时间,超时后返回falsenull

常见阻塞队列的实现类

BlockingQueue有多种实现,适用于不同场景:

1. ArrayBlockingQueue
  • 特点:基于数组实现的有界阻塞队列,必须指定容量大小。
  • 线程安全性:通过内部锁机制确保线程安全。
  • 使用场景:常用于生产者-消费者模式中限制资源数量的场景。

示例

BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);  // 容量为3的队列

// 生产者线程
new Thread(() -> {
    try {
        queue.put(1);
        System.out.println("生产者放入: 1");
        queue.put(2);
        System.out.println("生产者放入: 2");
        queue.put(3);
        System.out.println("生产者放入: 3");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

// 消费者线程
new Thread(() -> {
    try {
        Thread.sleep(1000);  // 模拟延迟
        System.out.println("消费者取出: " + queue.take());
        System.out.println("消费者取出: " + queue.take());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();
2. LinkedBlockingQueue
  • 特点:基于链表实现,可以指定容量大小,如果不指定则为无界队列。
  • 线程安全性:通过内部锁机制确保线程安全。
  • 使用场景:适合高并发场景下的大量数据交换,可以避免由于容量限制导致的过多阻塞。

示例

BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();  // 默认无界队列

// 生产者线程
new Thread(() -> {
    try {
        queue.put(1);
        System.out.println("生产者放入: 1");
        queue.put(2);
        System.out.println("生产者放入: 2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

// 消费者线程
new Thread(() -> {
    try {
        System.out.println("消费者取出: " + queue.take());
        System.out.println("消费者取出: " + queue.take());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();
3. PriorityBlockingQueue
  • 特点:基于优先级的无界阻塞队列,元素会根据自然顺序或提供的Comparator排序。没有容量限制,内部使用二叉堆实现。
  • 线程安全性:通过内部锁机制确保线程安全。
  • 使用场景:适用于需要优先级处理任务的场景,例如任务调度。

示例

BlockingQueue<Integer> queue = new PriorityBlockingQueue<>();

// 生产者线程
new Thread(() -> {
    try {
        queue.put(5);
        System.out.println("生产者放入: 5");
        queue.put(1);
        System.out.println("生产者放入: 1");
        queue.put(3);
        System.out.println("生产者放入: 3");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

// 消费者线程
new Thread(() -> {
    try {
        System.out.println("消费者取出: " + queue.take());  // 最先取出优先级高的元素
        System.out.println("消费者取出: " + queue.take());
        System.out.println("消费者取出: " + queue.take());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();
4. SynchronousQueue
  • 特点:不存储元素的阻塞队列,每个插入操作必须等待一个相应的移除操作,反之亦然。是一个"直接交换"的队列。
  • 线程安全性:通过内部锁机制确保线程安全。
  • 使用场景:适用于传递性场景,常用于生产者-消费者模型中一个生产者一个消费者的情况。

示例

BlockingQueue<String> queue = new SynchronousQueue<>();

// 生产者线程
new Thread(() -> {
    try {
        queue.put("消息1");
        System.out.println("生产者发送: 消息1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

// 消费者线程
new Thread(() -> {
    try {
        System.out.println("消费者接收: " + queue.take());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();
5. DelayQueue
  • 特点:实现了Delayed接口的元素可以按指定的延迟时间存储,只有当元素的延迟时间到达后,才能从队列中获取。常用于定时任务调度。
  • 线程安全性:通过内部锁机制确保线程安全。
  • 使用场景:适用于延迟任务执行的场景,如任务调度、消息推送等。

示例

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

class DelayedElement implements Delayed {
    private long delayTime;
    private String message;

    public DelayedElement(String message, long delayTime) {
        this.message = message;
        this.delayTime = System.currentTimeMillis() + delayTime;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
    }

    @Override
    public String toString() {
        return message;
    }
}

public class DelayQueueExample {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<DelayedElement> queue = new DelayQueue<>();

        queue.put(new DelayedElement("延迟消息1", 5000));  // 延迟5秒
        queue.put(new DelayedElement("延迟消息2", 10000)); // 延迟10秒

        while (!queue.isEmpty()) {
            DelayedElement element = queue.take();  // 按延迟时间依次取出
            System.out.println("取出消息: " + element);
        }
    }
}

总结

阻塞队列在并发编程中发挥了重要作用,尤其适合生产者-消费者模型。根据不同的需求场景,可以选择合适的阻塞队列实现来处理并发任务,例如:

  • ArrayBlockingQueue:适合需要有限资源的场景。
  • LinkedBlockingQueue:适合大规模并发的数据交换。
  • PriorityBlockingQueue:适合需要优先级处理的任务。
  • SynchronousQueue:适合直接传递任务的场景。
  • DelayQueue:适合延时任务调度。

合理使用阻塞队列可以极大地简化并发程序的实现,提升系统性能和安全性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值