mq - 阻塞队列BlockingQueue

队列

Queue

在 java 中,继承自 Collection,多数的实现类位于 java.util.concurrent,与线程池位于同一个包下,大多数的队列都与线程和锁挂钩,少量位于 java.util,比如 LinkedList、PriorityQueue。

程序员最早接触的队列一般是 LinkedList,其实它不仅是一个 List,同时也是一个 Queue(也是双端队列),但是它并不是一个线程安全的 Queue,不适合在多线程访问下使用。

阻塞队列

BlockingQueue 是一个接口,BlockingQueue 直译就是阻塞队列,阻塞队列的实现类是线程安全的,常用于 “消息生产发布” 场景。

应用场景:定义一个队列,多个线程负责添加数据,多个线程从中获取数据,所有线程都是同时执行的,如果集合已满,则生产者等待,如果集合为空,那么消费者等待(实际就是经典的 "生产者/消费者" 场景了),

对程序员的要求偏高,一般不会直接使用,结合多线程的场景,一般会直接使用 ThreadPoolTaskExecutor 等类似工具。

队列(Queue)常用函数

  • add(e):添加元素;
  • offer(e):有容量限制的时候,优先使用此函数;与 add() 相比,超出容量时,不会抛出异常,而是返回 false;
  • remove():删除第一个元素,何为第一个取决于数据结构;
  • poll():删除第一个元素,与 remove() 相比,集合为空时,不会抛出异常,而是返回 null;
  • element():获取第一个元素;
  • peek():获取第一个元素,与 element() 相比,没有元素时,不会抛出异常,而是返回 null;

堆栈(Stack)常用函数

(jdk 中似乎没有严格意义上的堆栈,堆栈的方法定义在双端队列(Deque)接口中)

  • push():插入到第一个元素;
  • pop():从尾端获取最后一个元素; |

ACK 机制

业务场景:

  • 队列中存在多种数据,处理线程需要确认数据类型,之后再正式取出数据;
  • 处理线程有意外关闭的可能,需要将数据退回队列,因此要等处理结束的时候,再正式取出数据;

解决方案:

element() 和 peek() 用于数据检测,仅仅只是取值,但是不删除元素,通常用于一些元素的校验。

(这个机制以后还会在消息队列中看到)

几个简单的队列

  • SynchronousQueue 译为同步队列,它也是一个阻塞队列。在代码的实现上,只允许存放一个元素,当存放第二个元素的时,线程进入等待,直到第一个元素被取出。
  • ArrayBlockingQueue 定长数组阻塞队列,内部由数组实现,因为队列需要经常对数据进行存取、排序,其效率反而要低于链表阻塞队列,使用的时候必须指定长度。
  • LinkedBlockingQueue 线性阻塞队列,内部由链表实现,不设置长度的情况,队列长度为Integer.MAX_VALUE,为防止队列过度扩展可以选择带参数的构造函数,其存取效率高于数组队列。

简单的消息轮询

/**
 * 消息处理接口回调
 * 
 * @author ChenSS on 2018年1月30日
 */
public interface Handler<T> {
    void handleMessage(T msg) throws Exception;

    void handleException(T msg, Exception e);
}


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

/**
 * 阻塞队列消息轮询
 * 
 * @author ChenSS on 2018年1月30日
 */
public class BlockingLopper<T> {
    private BlockingQueue<T> queue;
    /**
     * 哑元元素,在不强制中断线程的情况下,用于告知通知轮询线程可以结束循环
     */
    public T dummy;
    private Handler<T> handler;

    public void setQueue(BlockingQueue<T> queue) {
        this.queue = queue;
    }

    public void setDummy(T dummy) {
        this.dummy = dummy;
    }

    public void setHandler(Handler<T> handler) {
        this.handler = handler;
    }

    /**
     * 发送消息
     */
    public void put(T value) throws InterruptedException {
        queue.put(value);
    }

    /**
     * 发送消息
     * 
     * @throws IllegalStateException
     *             通常表示队列已满,不允许添加新的元素
     */
    public void add(T value) {
        queue.add(value);
    }

    /**
     * 发送消息
     * 
     * @throws RuntimeException
     *             代码存在部分未检查异常
     */
    public boolean offer(T value) {
        if (!queue.offer(value)) {
            try {
                // 简单的失败处理策略,当前线程等待0.5秒,重新尝试添加该元素
                // 以实际开发为准,在非必要的情况不要阻塞生产线程
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return this.offer(value);
        }
        return true;
    }

    /**
     * 发送消息
     * 
     * @throws InterruptedException
     *             等待时被中断
     */
    public void offer(T value, long timeout) throws InterruptedException {
        queue.offer(value, timeout, TimeUnit.MILLISECONDS);
    }

    /**
     * 放置哑元元素,通知线程结束(取出所有元素后结束)
     */
    public void stopLoop() throws InterruptedException {
        queue.put(this.dummy);
    }

    /**
     * 启动线程,开始轮询消息
     */
    public Thread loop() {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                T t = null;
                for (;;) {
                    try {
                        t = queue.take();
                        if (dummy.equals(t)) {
                            return;
                        } else {
                            handler.handleMessage(t);
                        }
                    } catch (Exception e) {
                        handler.handleException(t, e);
                    }
                }
            }
        });
        thread.start();
        return thread;
    }

    /**
     * 启动守护线程,开始轮询消息
     */
    public Thread loopDeamon() {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                T t = null;
                for (;;) {
                    try {
                        t = queue.take();
                        handler.handleMessage(t);
                    } catch (Exception e) {
                        handler.handleException(t, e);
                    }
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
        return thread;
    }
}
import java.util.concurrent.ArrayBlockingQueue;
/**
 * 测试函数
 */
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Handler<String> handler = new Handler<String>() {

            @Override
            public void handleMessage(String msg) throws Exception {
                Thread.sleep(1000);
                System.out.println(msg);
            }

            @Override
            public void handleException(String msg, Exception e) {
                e.printStackTrace();
            }
        };

        BlockingLopper<String> lopper = new BlockingLopper<String>();

        lopper.setDummy(new String());
        lopper.setHandler(handler);
        //指定不同的阻塞队列进行测试
        lopper.setQueue(new ArrayBlockingQueue<>(3));
        lopper.loop();

        lopper.offer("AAAA");
        lopper.offer("BBBB");
        lopper.offer("CCCC");
        lopper.offer("DDDD");
        System.out.println("--------------------");
        lopper.stopLoop();
    }
}

posted on   疯狂的妞妞  阅读(333)  评论(0编辑  收藏  举报

(评论功能已被禁用)
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示