除了使用迭代器对 list/set 进行更改之外,是否有线程安全的替代方法?
Is there a thread safe alternative to using iterator to make changes to a list/set?
这是我的代码:
class Processor implements Runnable {
private int id;
private Integer interaction;
private Set<Integer> subset;
public volatile static AtomicBoolean notRemoved = new AtomicBoolean(true);
public Object<E> dcp;
public Iterator<Integer> iterator;
public Processor(int id, Integer interaction, Set<Integer> subset, Object<E> dcp, Iterator<Integer> iterator) {
this.id = id;
this.interaction = interaction;
this.subset= subset;
this.dcp = dcp;
this.iterator = iterator;
}
public void run() {
while (Processor.notRemoved.get()){
System.out.println("Starting: " + this.id);
if (this.dcp.PA.contains(this.interaction)){
this.subset.add(this.interaction);
this.dcp.increaseScore(this.subset);
if (!this.subset.contains(this.interaction) && Processor.notRemoved.get()){
Processor.notRemoved.set(false);
System.out.println(iterator);
iterator.remove();
}
}
System.out.println("Completed: " + this.id);
}
}
}
public class ConcurrentApp {
public void multiFunction(Object<E> dcp, int threads) {
ExecutorService executor = Executors.newFixedThreadPool(threads);
int i =1;
while ((dcp.PA.size() > i) && (i <= dcp.R)){
for (Iterator<Integer> iterator = dcp.PA.iterator(); iterator.hasNext();){
Integer interaction = iterator.next();
ArrayList<Integer> removed = new ArrayList<Integer>(dcp.PA);
removed.remove(interaction);
ArrayList<Set<Integer>> subsets = dcp.getSubsets(removed, i);
for (int j = 0; j< subsets.size(); j++){
executor.submit(new Processor(j, interaction, subsets.get(j), dcp, iterator));
}
Processor.notRemoved.set(true); //Pretty sure this is necessary
}
i++;
}
executor.shutdown();
System.out.println("All tasks submitted");
try {
executor.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All tasks completed");
}
}
如我所料,我得到 java.util.ConcurrentModificationException 因为多个线程试图同时访问我的迭代器对象。我看到了很多使用 ArrayList 的替代方法(这就是 dcp.PA 字段)。我尝试使用 CopyOnWriteArrayList,但由于某些原因,当我引入 ArrayList 的线程安全实现时,我不断遇到更多问题。我想知道是否有人可以指出我应该使用哪种线程安全数据结构以及我应该如何在我的代码或其他解决方案中使用它们来解决并发修改错误的正确方向。
也许是时候停止提问了,"How do I use this particular technique?" 开始提问,"How do I accomplish my goal?" 您使用作为参数传入的迭代器的技术非常脆弱。不需要,真的。
抵制将迭代器作为参数传递的诱惑,更不用说传递给多个线程了。这是一个很大的禁忌。您几乎是在明确要求 ConcurrentModificationException
。迭代器迭代什么集合?您的 Processor
class 中没有任何内容可以强制执行一致性。哦,我知道你有迭代器连接到 ConcurrentApp
中的集合,但是 Processor
方法是 public,因此 Processor
不能信任这样的连接。
而 notRemoved
令人困惑。 "removed" 到底是什么意思?当您的算法中没有其他内容时,我也非常怀疑它是 static
。
所以你的答案的开头部分是清除代码中的所有反模式。将迭代器与生成它们的变量保持在同一范围内。遵循命名约定。如果您需要帮助,请在此处显示所有相关信息。
一旦你重构了你的代码并消除了所有的怪异之处,让删除成为线程安全的就会变得容易得多。
void processRemoval(Collection<E> collection) {
synchronized(lock) {
for (Iterator<E> iter = collection.iterator(); iter.hasNext(); ) {
final E element = iter.next();
if (shouldRemove(element) {
iter.remove();
}
}
}
}
HTH
这是我的代码:
class Processor implements Runnable {
private int id;
private Integer interaction;
private Set<Integer> subset;
public volatile static AtomicBoolean notRemoved = new AtomicBoolean(true);
public Object<E> dcp;
public Iterator<Integer> iterator;
public Processor(int id, Integer interaction, Set<Integer> subset, Object<E> dcp, Iterator<Integer> iterator) {
this.id = id;
this.interaction = interaction;
this.subset= subset;
this.dcp = dcp;
this.iterator = iterator;
}
public void run() {
while (Processor.notRemoved.get()){
System.out.println("Starting: " + this.id);
if (this.dcp.PA.contains(this.interaction)){
this.subset.add(this.interaction);
this.dcp.increaseScore(this.subset);
if (!this.subset.contains(this.interaction) && Processor.notRemoved.get()){
Processor.notRemoved.set(false);
System.out.println(iterator);
iterator.remove();
}
}
System.out.println("Completed: " + this.id);
}
}
}
public class ConcurrentApp {
public void multiFunction(Object<E> dcp, int threads) {
ExecutorService executor = Executors.newFixedThreadPool(threads);
int i =1;
while ((dcp.PA.size() > i) && (i <= dcp.R)){
for (Iterator<Integer> iterator = dcp.PA.iterator(); iterator.hasNext();){
Integer interaction = iterator.next();
ArrayList<Integer> removed = new ArrayList<Integer>(dcp.PA);
removed.remove(interaction);
ArrayList<Set<Integer>> subsets = dcp.getSubsets(removed, i);
for (int j = 0; j< subsets.size(); j++){
executor.submit(new Processor(j, interaction, subsets.get(j), dcp, iterator));
}
Processor.notRemoved.set(true); //Pretty sure this is necessary
}
i++;
}
executor.shutdown();
System.out.println("All tasks submitted");
try {
executor.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All tasks completed");
}
}
如我所料,我得到 java.util.ConcurrentModificationException 因为多个线程试图同时访问我的迭代器对象。我看到了很多使用 ArrayList 的替代方法(这就是 dcp.PA 字段)。我尝试使用 CopyOnWriteArrayList,但由于某些原因,当我引入 ArrayList 的线程安全实现时,我不断遇到更多问题。我想知道是否有人可以指出我应该使用哪种线程安全数据结构以及我应该如何在我的代码或其他解决方案中使用它们来解决并发修改错误的正确方向。
也许是时候停止提问了,"How do I use this particular technique?" 开始提问,"How do I accomplish my goal?" 您使用作为参数传入的迭代器的技术非常脆弱。不需要,真的。
抵制将迭代器作为参数传递的诱惑,更不用说传递给多个线程了。这是一个很大的禁忌。您几乎是在明确要求 ConcurrentModificationException
。迭代器迭代什么集合?您的 Processor
class 中没有任何内容可以强制执行一致性。哦,我知道你有迭代器连接到 ConcurrentApp
中的集合,但是 Processor
方法是 public,因此 Processor
不能信任这样的连接。
而 notRemoved
令人困惑。 "removed" 到底是什么意思?当您的算法中没有其他内容时,我也非常怀疑它是 static
。
所以你的答案的开头部分是清除代码中的所有反模式。将迭代器与生成它们的变量保持在同一范围内。遵循命名约定。如果您需要帮助,请在此处显示所有相关信息。
一旦你重构了你的代码并消除了所有的怪异之处,让删除成为线程安全的就会变得容易得多。
void processRemoval(Collection<E> collection) {
synchronized(lock) {
for (Iterator<E> iter = collection.iterator(); iter.hasNext(); ) {
final E element = iter.next();
if (shouldRemove(element) {
iter.remove();
}
}
}
}
HTH