BlockingQueue

昨天研究了下quartz定时任务,在看xml的时候发现下面还配置了邮件的定时发送

再贴下目标类EmailQueueService的代码,这里看到了今天的主角BlockingQueue,就知道学习的机会又来了--容我明天再百度会再来续。。。这个讲的挺好的https://www.cnblogs.com/KingIceMou/p/8075343.html


BlockingQueue有四个具体的实现类,常用的两种实现类为:

1、ArrayBlockingQueue:一个由数组支持的有界阻塞队列,规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的。

2、LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的。

LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。

LinkedBlockingQueue和ArrayBlockingQueue区别:
LinkedBlockingQueue和ArrayBlockingQueue比较起来,它们背后所用的数据结构不一样,导致LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue.

public class EmailQueueService {

    public void excute() {
        this.sendMail(email);
    }

    //缓存发送邮件的队列
    public static BlockingQueue<EmailBO> queue = new ArrayBlockingQueue<EmailBO>(2000);
    //发送邮件线程状态
    private static boolean isRunning = false;
    //超时时间间隔
    public static long TIMEOUT_INTERVAL = 3 * 60 * 1000;

    public boolean sendMail(EmailBO mail) {
        boolean rsp = false;
        try {
            //阻塞3秒
            rsp = queue.offer(mail);
            if (!rsp) {
                throw new Exception("服务器发送邮件繁忙,请稍后再发");
            }
            if (!isRunning) {
                startup();
                isRunning = true;
            }
        } catch (Exception e) {
            logger.error("添加发送邮件队列出错", e);
        }
        return rsp;
    }

    public void startup() {
        if (isRunning) {
            return;
        }
        Thread th = new Thread() {
            @Override
            public void run() {
                logger.debug("邮件发送消息线程启动");
                long timestamp = System.currentTimeMillis();
                while (isRunning) {

                    try {
                        if (queue.size() == 0) {
                            Thread.sleep(1000);
                            isRunning = false;
                            logger.debug("邮件发送线程停止。");
                            break;
                        }
                        timestamp = System.currentTimeMillis();
                        EmailBO mail = queue.poll();
                        send(mail);
                        //发送一个邮件休息200毫秒,防止发送过快,导致主邮箱被锁定
                        Thread.sleep(200);

                    } catch (Exception e) {
                        logger.error("邮件推送线程出错", e);
                    }
                }
                isRunning = false;
                logger.debug("邮件发送线程停止。");
            }
        };
        th.start();
    }

    @RpcService
    public void send(EmailBO email) {

            //发送邮件
            Mail mail = new Mail();
            mail.setTitle(email..getBt())
            boolean issend = false;
            try {
                issend = MailFactory.sendHtmlMail(mail);
            } catch (RpcException e) {
                logger.error(e.toString());


            }
        
    }
}

后续补充:

    在实际项目中,队列的使用,感觉是为了项目更好的异步执行,对于业务逻辑处理麻烦(如需远程调用接口)又不影响主流程的,可单独放入线程执行(如上面的邮件发送)。可把需要处理的数据流先放入队列里,主流程可直接往下执行。队列里的数据可通过线程监听队列的数据来处理。创建线程类,队列作为线程类内部对象,当队列中放入数据,线程就会去拉取处理数据。

    //创建一个消息处理队列
    public final static BlockingQueue<KafkaMessage> MESSAGE_BLOCKING_QUEUE = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);

    @Override
    public void run() {
        logger.info("process data message thread start+ " + Thread.currentThread());
        while (!isClose) {
            try {
                KafkaMessage kafkaMessage = MESSAGE_BLOCKING_QUEUE.take();
                handleMessage(kafkaMessage);
            } catch (Exception e) {
                logger.error("event handler occur exception!",e);
            }
        }
        logger.info( "process event thread end");
    }

    建议使用take()函数去拉取队列中的数据,如果队列中没有数据,则线程wait释放CPU,而poll()则不会等待,直接返回null;同样,空间耗尽时offer()函数不会等待,直接返回false,而put()则会wait,因此如果你使用while(true)来获得队列元素,千万别用poll(),CPU会100%,导致系统崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值