开发笔记——LinkedBlockingQueue

本文详细解析了在小米4等部分机型上应用闪退的原因,深入探讨内存溢出问题,特别是与GL2JNIView类及YUVBufferThread相关的线程创建时的内存管理。通过对比take和poll方法,提出了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近工作上收到一个测试问题,在小米4等部分机型上应用容易闪退。然后查到原因是内存溢出,这里记录一下。

错误信息:

这里写图片描述

然后定位到GL2JNIView这个类
这里写图片描述
根据错误信息可以知道是创建线程时出现内存溢出,然后再看一下YUVThread

    class YUVBufferThread extends Thread {
        private YUVBuffer yuvBuf;
        private boolean isStopThread;

        public void stopThread(){
            isStopThread = true;
        }

        @Override
        public void run() {
            while (GL2JNIView.this.startReceiveBuf && !this.isStopThread) {
                try {
                    if (mBufQueue != null){
                        yuvBuf = mBufQueue.take();
                    }
                    if (yuvBuf != null) {
                        synchronized (lockObj) {

                            if (!m_is3d) {
                                if (y != null && u != null && v != null) {
                                    GL2JNIView.this.y.clear();
                                    GL2JNIView.this.u.clear();
                                    GL2JNIView.this.v.clear();
                                    yuvBuf.buf.position(0);
                                    yuvBuf.buf.get(yByteArr, 0, yByteArr.length);
                                    yuvBuf.buf.get(uByteArr, 0, uByteArr.length);
                                    yuvBuf.buf.get(vByteArr, 0, vByteArr.length);

                                    yuvBuf.buf.position(0);
                                    GL2JNIView.this.y.put(yByteArr, 0, yByteArr.length);
                                    GL2JNIView.this.u.put(uByteArr, 0, uByteArr.length);
                                    GL2JNIView.this.v.put(vByteArr, 0, vByteArr.length);
                                }
                            } else {
                                int size = yuvBuf.buf.limit();
                                if (m_disOld != true && yuvByteArr != null /*&& size>=yByteArr.length+uByteArr.length+vByteArr.length*/)//m_disOld
                                {
                                    yuvBuf.buf.position(0);
                                    yuvBuf.buf.get(yuvByteArr, 0, yByteArr.length + uByteArr.length + vByteArr.length);
                                    yuvBuf.buf.position(0);
                                } 
                            }
                            yuvBuf = null;
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

问题在这行代码:
yuvBuf = mBufQueue.take();
这里使用了一个LinkedBlockingQueue队列不断的取出YUV数据,这里使用take()方式取数据就出现了问题,当队列里面没有元素时,它会阻塞线程从而导致线程不能退出。
下面贴一下take的源码:

/**
     * Retrieves and removes the head of this queue, waiting if necessary
     * until an element becomes available.
     *
     * @return the head of this queue
     * @throws InterruptedException if interrupted while waiting
     */
    E take() throws InterruptedException;

除了take方法,还有poll方法,同样看一下源码注释:

/**
     * Retrieves and removes the head of this queue, waiting up to the
     * specified wait time if necessary for an element to become available.
     */
    E poll(long timeout, TimeUnit unit)
        throws InterruptedException;

这个方法能设置阻塞时间,所以这里使用poll方法代替take就能解决问题了。

总结##

在开发中要多去看源码,这样我们才能更好的理解没一个方法的作用,从而使用正确的Api来实现我们的需求。

### Java `LinkedBlockingQueue` 使用方法 #### 创建 `LinkedBlockingQueue` 可以通过无参构造函数创建一个具有默认容量(`Integer.MAX_VALUE`)的队列,也可以通过指定容量的方式创建: ```java // 默认容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(); // 容量为 10 的 LinkedBlockingQueue LinkedBlockingQueue<Integer> boundedQueue = new LinkedBlockingQueue<>(10); ``` [^2] #### 基本操作 - **插入元素** 向队列中添加元素的方法有多种选择。对于不会抛出异常的操作,当尝试往已满的队列中加入新项时会返回特定的结果;而对于可能会抛出异常的情况,则会在违反约束条件时立即报错。 ```java boolean offerResult = queue.offer("item"); // 如果成功则返回 true, 否则 false (不等待) try { boolean timedOfferResult = queue.offer("another item", 5L, TimeUnit.SECONDS); // 尝试最多五秒的时间去放置项目 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } ``` [^1] - **移除元素** 从队列头部取出并删除元素也有几种不同的方式可以选择,具体取决于是否希望在遇到空队列的情况下得到提示还是愿意无限期地等待直至可用为止。 ```java String pollItem = queue.poll(); // 获取头元素并移除此元素; 若为空则返回 null 或者超时期间未找到任何东西就返回null try { String takeItem = queue.take(); // 取得下一个待处理的任务,如果没有任务存在就会一直阻塞当前线程直到有新的条目到来 } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } // 移除所有元素 queue.clear(); ``` - **查询队列状态** 有时需要检查队列的状态而不做修改动作,比如查看是否有剩余空间或者确认某个对象是否存在其中。 ```java int size = queue.size(); // 返回队列中的元素数目 boolean containsElement = queue.contains("test element"); ``` [^4] #### 生产者-消费者模式示例 下面是一个简单的例子展示了如何利用 `LinkedBlockingQueue` 实现经典的生产者-消费者问题解决方案: ```java class Producer implements Runnable { private final BlockingQueue<String> queue; public Producer(BlockingQueue<String> q) { this.queue = q; } @Override public void run() { try { while(true){ String product = "product-" + System.currentTimeMillis(); queue.put(product); System.out.println(Thread.currentThread().getName()+" produced "+product); Thread.sleep((long)(Math.random()*100)); } }catch(Exception ex){ /* handle exception */ } } } class Consumer implements Runnable { private final BlockingQueue<String> queue; public Consumer(BlockingQueue<String> q) {this.queue=q;} @Override public void run(){ try{ while(true){ String consumedProduct=queue.take(); System.out.println(Thread.currentThread().getName()+" consumed "+consumedProduct); Thread.sleep((long)(Math.random()*100)); } }catch(Exception ex){/*handle exception*/} } } public class Main { public static void main(String[] args)throws Exception{ int capacity = 10; LinkedBlockingQueue<String> sharedQueue=new LinkedBlockingQueue<>(capacity); ExecutorService executor=Executors.newCachedThreadPool(); for(int i=0;i<3;++i)//启动三个生产者线程 executor.execute(new Producer(sharedQueue)); for(int j=0;j<2;++j)//启动两个消费者线程 executor.execute(new Consumer(sharedQueue)); Thread.sleep(5000);//让程序运行一段时间后结束 executor.shutdownNow();//关闭执行服务 } } ``` [^5]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值