首页 > 编程知识 正文

arraylist转string,避免踩坑

时间:2023-05-04 08:43:19 阅读:165144 作者:3287

一.背景日前,开发了一个可收集本机原始GNSS数据并支持高频率IMU数据写入的Flutter插件。 设计了缓存池,缓存3分钟收集的日志信息,采用多线程添加数据,每分钟执行一次计划任务,清理过期数据。 为了省事,我直接使用了CopyOnWriteArrayList缓存字符串。 在后续使用中,发现后台频繁出现gc回收垃圾日志,经过排查,定位于该并发类。 通过阅读源代码,我了解了这个漏洞原本是由于自己不理解造成的,同时为什么copyonwritearrraylist读写不多

二、意思从CopyOnWriteArrayList的字义可以看出,这是写时复制的ArrayList,如果容器需要修改,不直接修改当前的容器,而是先把当前的容器复印出来

对于旧容器来说是不变的,因为容器在每次修改时都会创建新副本。 这意味着线程安全,不需要锁定等同步操作。 所以我们可以利用CopyOnWriteArrayList的不变性进行同时读取操作。

CopyOnWriteArrayList的所有修改操作(add、 set等)是通过制作基础数组的新副本来实现的,因此CopyOnWrite容器也是读写分离思想的体现,由于读写使用不同的容器,所以在写入时不会阻塞读取操作,进行读写

三、源代码分析说了这么多,我遇到的漏洞到底来自哪里? 我想已经有同学从我刚才的描述中推测出来了,让我们从源代码的角度直接分析问题吧。

以下源代码分析基于Android 11

Add我们先来看看添加操作

公共void add (intindex,E element )同步(lock ) { Object[] elements=getArray ); int len=elements.length; 索引len|| if (索引0 ) thrownewindexoutofboundsexception (out of bounds )索引,len ); 对象[ ] new elements; int numMoved=len - index; if(nummoved==0) new elements=arrays.copy of (elements,len 1); else { new elements=new object [ len1]; system.arraycopy(elements,0,newElements,0,index ); system.arraycopy(elements,index,newElements,index 1,numMoved ); } newElements[index]=element; setarray (新元素; }首先,使用sync关键字包装代码块,并锁定了添加元素的逻辑。 这样做的好处是,在最后一次添加完成并解除锁定之前,后续的add操作无法执行。

getArray ) )返回由volatile限定的数组,volatile也是当前list中的实际元素,以确保所有线程看到的数据的一致性。

其次,也应该学习一下对index的异常判断。 对于从外部传递的参数,需要考虑异常情况的处理。

然后,len-index确定是在数组的中间插入元素,还是在数组的末尾插入元素。 这里使用arraycopy进行数据的复制,确保插入的空间。 然后,对新数组的索引进行值的设定。

最后,对新数组的引用指向原始数组。

让我们来看看removeremove的操作

publiceremove(intindex ) synchronized (lock ) { Object[] elements=getArray ); int len=elements.length; eoldvalue=get(elements,index ); int numMoved=len - index - 1; num moved==0) setarray(Arrays.copyof ) elements,len - 1 ); else { object [ ] new elements=new object [ len-1 ]; system.arraycopy(elements,0,newElements,0,index ); system.arraycopy(elements,index 1,newElements,index,numMoved ); setarray (新元素; } return oldValue; 与Add方法类似,但唯一的不同在于缩小了数组。 这里不说明。 但是,numMoved简单地减少1的原因是,数组必须减少1,因为它以0作为起始索引,其中len等于elements.length。

分析以上两段代码可以清楚地看出,在复制、删除操作时存储器中同时存在多个存储器对象。 这样,在list元素很多的情况下,除了消耗大量的CPU资源之外,对存储器的开销也不小。 因此,会在背景频繁地看到GC。

四、在解决方案的最后谈谈我是如何解决开头的问题的吧。

必须对队列的第一个节点执行remove操作,删除旧数据,然后将新数据添加到队列末尾,因此将CopyOnWriteArrayList替换为LinkedList,并使用sync同步锁限定添加和删除操作。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。