栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

使用foreach遍历ArrayList时的线程安全

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用foreach遍历ArrayList时的线程安全

通常,对不是线程安全的数据结构进行并发操作是一个非常糟糕的主意。您无法保证将来的实现不会改变,这可能会严重影响应用程序的运行时行为,即java.util.HashMap被并发修改时会导致无限循环。

为了同时访问List,Java提供了

java.util.concurrent.CopyOnWriteArrayList
。使用此实现将以多种方式解决您的问题:

  • 它是线程安全的,允许并发修改
  • 遍历列表快照不受并发添加操作的影响,从而允许并发添加和迭代
  • 比同步快

或者,如果 使用 内部 数组的副本是 严格的
要求(我无法想象您的情况,则该数组相当小,因为它仅包含对象引用,可以非常有效地将其复制到内存中),您可以同步地图上的访问。但这将需要正确初始化Map,否则您的代码可能会抛出a,

NullPointerException
因为不能保证线程执行的顺序(您假设
populateList()
之前已开始执行,因此列表已初始化。使用同步块时,请选择保护的明智地进行阻止。
run()

在同步块中使用方法,读取器线程必须等待,直到处理完游标的结果为止(这可能需要一段时间),因此您实际上失去了所有并发性。

如果您决定使用同步块,则将进行以下更改(并且我不主张它们是完全正确的):

初始化列表字段,以便我们可以同步对其的访问:

private List<String> mList = new ArrayList<>(); //initialize the field

同步修改操作(添加)。不要从同步块内部的游标中读取数据,因为如果它是低延迟操作,则在该操作期间无法读取mList,这会阻塞所有其他线程一段时间。

//mList = new ArrayList<>(); remove that line in your preString data = cursor.getString(cursor.getColumnIndex(DataProvider.NAME)); //do this before synchronized block!synchronized(mList){  mList.add(data);}

读取迭代必须在同步块内部,因此在迭代时不会添加任何元素:

synchronized(mList){   for (String name : mList) {    if (name.equals(query) {      return true;    }  }}

因此,当两个线程在列表上运行时,一个线程可以添加单个元素,也可以一次遍历整个列表。您没有在代码的这些部分上并行执行。

关于名单(即同步的版本

Vector
Collections.synchronizedList()
)。那些性能可能较差,因为在同步时您实际上失去了简单执行,因为一次只能有一个线程运行受保护的块。此外,它们可能仍然容易出现
ConcurrentModificationException
,甚至可能在单个线程中发生。如果在迭代器创建之间修改了数据结构,则应该抛出该异常。因此,这些数据结构不会解决您的问题。

我也不建议手动同步,因为简单地做错了的风险就太大了(在错误或不同的监视上进行同步,太大的同步块,…)

TL; DR

用一个

java.util.concurrent.CopyOnWriteArrayList



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/454578.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号