gpt4 book ai didi

java并发: CopyOnWriteArrayList strategy

转载 作者:行者123 更新时间:2023-11-30 01:58:04 25 4
gpt4 key购买 nike

我试图将 CopyOnWriteArrayList 理解为我的代码:

我的代码是:

public class AuditService {
private CopyOnWriteArrayList<Audit> copyWrite;

public void flush(Audit... audits) {
Collection<Audit> auditCollection = Arrays.asList(audits);
this.copyWrite.addAll(auditCollection);

this.copyWrite.forEach(audit -> {
try {
// save audit object on database
this.copyWrite.remove(audit);
} catch (DataAccessException e) {
// log it
}

});
}
}

这段代码的作用是:

  1. 首先将审核存储到缓冲区中,CopyOnWriteArrayList
  2. 尝试将审核保存到数据库
  3. 存储后,会将其从缓冲区 CopyOnWriteArrayList 中删除。

其他:

  1. AuditService 是一个单例类
  2. flush 方法可以由多个线程访问。

问题:

  1. 我猜想 this.copyWrite.forEach(audit -> {... 可以由多个线程同时到达:这是否意味着可以尝试保存相同的审核对象数据库两次?
  2. 每次对CopyOnWriteArrayList进行修改操作时,都会在其他线程上填充一个新副本?它是如何填充的?

最佳答案

每次调用remove时,都会创建一个支持CopyOnWriteArrayList的内部数组的新副本。将来使用访问器和修改器方法访问该列表将可以看到更新。

但是CopyOnWriteArrayList#foreach 方法会迭代调用它时可用的数组。这意味着在列表上的任何更新之前进入foreach的方法flush的所有执行都将迭代数组的过时版本。

因此,在并行执行方法 flush 期间,相同的 Audit 元素将被多次保留,最多 d times if d 是方法 flush 的最大并发执行次数。

在这种情况下使用 CopyOnWriteArrayList 的另一个问题是,由于每次调用 remove 都会创建一个新副本,因此代码的复杂度为 d.n ^2 其中 n 是列表的长度,d 是上面定义的。

CopyOnWriteArrayList 不是此处使用的正确实现。存在多种可能的合适设计。其中之一是使用LinkedBlockingQueue,如下所示[*]:

public void flush(Audit... audits) {
Collection<Audit> auditCollection = Arrays.asList(audits);
this.queue.addAll(auditCollection);

Collection<Audit> poissonedAudits = new ArrayList<Audit>();
Audit audit = null;
while ((audit = this.queue.poll()) != null) {
try {
// save audit object on database
queue.remove(audit);
} catch (DataAccessException e) {
// log it
poissonedAudits.add(audit);
}
}
this.queue.addAll(poissonedAudits);
}

LinkedBlockingQueue#poll() 的调用是线程安全且原子的。同一元素永远不能被多次轮询(只要它不被多次添加到队列中,参见 [*])。复杂度与 n 呈线性关系。

需要考虑的两点:

  • 您的审核集合不得包含 null 元素,因为 LinkedBlockingQueue 禁止这样做,因为 null 用于指示列表是空的。
  • 您应该注意不要使用阻塞方法,例如 poll(timeout, unit)take 来轮询队列。

[*] flush 方法发生了变化,new 不执行 Audit 元素的副本。我不确定如果并行调用 flush 方法,是否可以保证这些元素是不同的。如果 Audit 数组对于所有 flush 调用都相同,则在调用 flush 之前,只应填充队列一次。

关于java并发: CopyOnWriteArrayList strategy,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53723990/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com