我调用了类ListIterator的remove方法的代码,并且我不明白为什么在运行列表时删除一个项之后,在尝试获取删除后的下一个元素时会抛出一个异常。以下是我所读到的资料来源:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
SubList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}我不明白的是:
。
2.为什么lastRet设置为-1?
3.下面的行是: expectedModCount = ArrayList.this.modCount;set *expectedModCount *等于modCount,因此在下一次迭代中,当两个变量都被检查是否相等时,if将“表示”true和everything ok。我在网上读了很多文章,其中有一些答案,但还是无法理解。
由于我得到的响应表明代码不会导致运行时异常,下面是导致此异常的代码:
public class Testing
{
public static void main(String[] args)
{
List <String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
Iterator<String> iterator = list.listIterator();
while (iterator.hasNext())
{
String st = iterator.next();
if (st.equals("c"))
{
list.remove(index);
}
else
{
System.out.println(st);
}
}
}发布于 2020-11-25 17:51:34
Removes from the underlying collection the last element returned by this iterator因此,游标在调用该方法时指向下一个元素,因此使用了lastRet。
额外问题:
我明白。就问个小问题。如果我有两个不同的线程在同一个列表上使用for/while循环运行(而不是使用Iterator)。其中之一删除一个项目,在这种情况下不会出现异常?只有当使用迭代器系统时才是“安全”的?
多线程不是一个“小”问题:)
简单的回答是,不,这两种情况下都不安全。无论您使用哪一个循环,由于一个线程正在检查执行另一个循环是否安全(是.hasNext()还是i< size()),另一个线程可以同时删除/添加元素。因此,如果使用for循环,则不会获得并发修改异常,但可能会得到超出范围的索引异常。
你应该自己试试。下面是一些令人讨厌的示例(我使用LinkedList进行快速删除操作,因为使用迭代器从列表的开头删除对象非常缓慢)
Iterator (抛出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块,确保每次只允许在内部执行一个线程--在输入同步块后,有必要再次检查条件是否仍然为真:
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 = 1000000https://stackoverflow.com/questions/65009058
复制相似问题