- Iterator.remove() 的 javadoc 说:
Removes from the underlying collection the last element returned by this iterator
所以当这个方法被调用时,光标指向下一个元素,因此使用了 lastRet。
-
-1 表示未找到。它设置为 -1 因为您刚刚删除了 lastRet 因此它不再存在。这意味着你不能在不先调用 next() 的情况下再次调用 remove():如果你查看 next(),它会更新 lastRet
-
调用 ArrayList.this.remove() 会更改 modCount(modCount 由 AbstractList 管理,它是您正在查看的 ArrayList 迭代器的超类)。由于此更改是由方法本身引起的,因此我们知道这是一个有效的更改(换句话说,我们只是请求更改列表)。所以局部变量(expectedModCount)被更新了。
如果这个列表被迭代器的另一个实例修改,那么 AbstractList 的 modCount 会改变,但属于当前实例的 expectedModCount 不会改变。这样当前实例就会知道有并发修改。
补充问题:
我明白了。请只是一个小问题.. 如果我有两个不同的线程在同一个列表上运行 for/while 循环(而不是使用迭代器)。其中一个人删除了一个项目,在这种情况下不会发生异常?只有在使用 Iterators 系统时才“安全”?
多线程不是一个“小”问题:)
简短的回答是,不,这两种情况都不安全。无论您使用什么循环,因为 1 个线程正在检查执行另一个循环是否安全(无论是 .hasNext() 还是 i
你应该自己试试。以下是一些 hacky 示例(我使用 LinkedList 进行快速删除操作,因为使用迭代器从列表开头删除对象非常慢)
迭代器(抛出 ConcurrentModificationException):
import java.util.*;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
List<Long> list = new LinkedList<>();
for (long i1 = 0; i1 < 1_000_000; i1++) {
list.add(i1);
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<Integer>> tasks = new ArrayList<>();
Iterator<Long> iterator = list.iterator();
for (int i = 0; i < 10; i++) {
tasks.add(() -> {
int count = 0;
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
count++;
}
System.out.println("Thread " + Thread.currentThread() + " removed " + count + " items");
return count;
});
}
List<Future<Integer>> results = executorService.invokeAll(tasks);
int sum = 0;
for (Future<Integer> result : results) {
sum += result.get();
}
System.out.println("sum of removed items = " + sum);
executorService.shutdown();
}
}
用 for i 循环替换(抛出 IndexOutOfBoundsException):
List<Long> list = new ArrayList<>();
for (long i1 = 0; i1 < 1_000_000; i1++) {
list.add(i1);
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<Integer>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
tasks.add(() -> {
int count = 0;
for (int j = 0; j < list.size(); j++) {
list.remove(list.size() - 1);
count++;
}
System.out.println("Thread " + Thread.currentThread() + " removed " + count + " items");
return count;
});
}
List<Future<Integer>> results = executorService.invokeAll(tasks);
int sum = 0;
for (Future<Integer> result : results) {
sum += result.get();
}
System.out.println("sum of removed items = " + sum);
executorService.shutdown();
要避免这些异常,您可以做的一件事是使用 synchronized 块,它确保一次只允许在内部执行 1 个线程 - 进入后需要再次检查同步阻塞条件是否仍然为真:
List<Long> list = new LinkedList<>();
for (long i1 = 0; i1 < 1_000_000; i1++) {
list.add(i1);
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<Integer>> tasks = new ArrayList<>();
Iterator<Long> iterator = list.iterator();
Object lock = new Object();
for (int i = 0; i < 10; i++) {
tasks.add(() -> {
int count = 0;
while (iterator.hasNext()) {
synchronized (lock) {
if (iterator.hasNext()) {
iterator.next();
iterator.remove();
count++;
}
}
}
System.out.println("Thread " + Thread.currentThread() + " removed " + count + " items");
return count;
});
}
List<Future<Integer>> results = executorService.invokeAll(tasks);
int sum = 0;
for (Future<Integer> result : results) {
sum += result.get();
}
System.out.println("sum of removed items = " + sum);
executorService.shutdown();
Java 还具有并发集合,您可以使用这些集合来启用多个线程进行并行计算。
为每个线程提供自己的迭代器需要更改为 ConcurrentLinkedQueue 之类的内容,以避免并发修改异常。但是,代码给出了一个非常错误的结果:
Collection<Long> list = new ConcurrentLinkedQueue<>();
for (long i1 = 0; i1 < 1_000_000; i1++) {
list.add(i1);
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<Integer>> tasks = new ArrayList<>();
Object lock = new Object();
for (int i = 0; i < 10; i++) {
tasks.add(() -> {
int count = 0;
Iterator<Long> iterator = list.iterator();
while (iterator.hasNext()) {
synchronized (lock) {
if (iterator.hasNext()) {
iterator.next();
iterator.remove();
count++;
}
}
}
System.out.println("Thread " + Thread.currentThread() + " removed " + count + " items");
return count;
});
}
List<Future<Integer>> results = executorService.invokeAll(tasks);
int sum = 0;
for (Future<Integer> result : results) {
sum += result.get();
}
System.out.println("sum of removed items = " + sum);
executorService.shutdown();
打印出来的
sum of removed items = 1105846
所以看起来迭代器不是很线程安全:)
如果我们去掉迭代器:
Queue<Long> list = new ConcurrentLinkedQueue<>();
for (long i1 = 0; i1 < 1_000_000; i1++) {
list.add(i1);
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<Integer>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
tasks.add(() -> {
int count = 0;
while (!list.isEmpty()) {
list.poll();
count++;
}
System.out.println("Thread " + Thread.currentThread() + " removed " + count + " items");
return count;
});
}
List<Future<Integer>> results = executorService.invokeAll(tasks);
int sum = 0;
for (Future<Integer> result : results) {
sum += result.get();
}
System.out.println("sum of removed items = " + sum);
executorService.shutdown();
这样更好:
sum of removed items = 1000007
放回锁:
Queue<Long> list = new ConcurrentLinkedQueue<>();
for (long i1 = 0; i1 < 1_000_000; i1++) {
list.add(i1);
}
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<Integer>> tasks = new ArrayList<>();
Object lock = new Object();
for (int i = 0; i < 10; i++) {
tasks.add(() -> {
int count = 0;
while (!list.isEmpty()) {
synchronized (lock) {
if (!list.isEmpty()) {
list.poll();
count++;
}
}
}
System.out.println("Thread " + Thread.currentThread() + " removed " + count + " items");
return count;
});
}
List<Future<Integer>> results = executorService.invokeAll(tasks);
int sum = 0;
for (Future<Integer> result : results) {
sum += result.get();
}
System.out.println("sum of removed items = " + sum);
executorService.shutdown();
似乎有效
sum of removed items = 1000000