List如何在Java中的大小小于零?

时间:2020-05-19 11:24:11

标签: java list

我们在Java应用程序日志中发现了此错误: java.lang。 IndexOutOfBoundsException :索引:0,大小:-1

at java.util.LinkedList.checkPositionIndex(LinkedList.java:560)
at java.util.LinkedList.listIterator(LinkedList.java:867)
at java.util.AbstractList.listIterator(AbstractList.java:299)
at java.util.AbstractSequentialList.iterator(AbstractSequentialList.java:239)
at //foreach loop over list

LinkedList的大小小于零怎么可能?如果该列表不是线程安全的,可能是并发问题吗?无论如何我们都无法复制它。

1 个答案:

答案 0 :(得分:4)

该异常跟踪中的“ -1”是由outOfBoundsMsg私有方法产生的,该方法只将字段size放在其中。

在相关方法中,通过sizesize++修改了size--字段。

留下以下解释:

  1. 损坏的VM。这似乎..不太可能。
  2. 内存损坏。这似乎..不太可能。
  3. 并发问题。

这是LinkedList(请参阅堆栈跟踪),它不是线程安全的(实际上,非常很少有用例,其中linkedlist是正确的答案,如果您在编程生涯中从未使用过LinkedList,可能使用的是正确的数量)。

并发问题被简单地解释了(任何给定的域可能会也可能不会-完全取决于虚拟机和月球的相位,这是薛定ed猫的行为方式:每个线程都有一个短暂的副本,他们修改,不时进行同步(可任意解决冲突)。

VM的设置方式应该编写代码,以使您无法观察到;不要编写例如预测任意同步力矩的代码,这是您无法做到的。

与大多数由并发问题引起的问题一样,要可靠地复制这样的东西非常困难。这取决于您的Winamp中播放的是音乐,而不是。

要解决此问题:。您可以尝试使用synchronized()块来保护 all 对该列表的所有访问,或者仅将列表包装在Collections.synchronizedList中,但这样做可以避免出现以字段结尾的奇怪情况包含无效值,这不可能真正解决任何问题。像这样的代码:

if (!list.contains(a)) list.add(a);

即使列表是“同步的”,

也永远无法正常工作:同步意味着任何单个调用都被认为是原子调用,但这就是对列表的2个调用。其他代码可以在contains调用和add调用中间随意添加“ a”:因此,为什么同步列表很少是您想要的。

您更可能希望从java.util.concurrent包中找到一份更适合这份工作的清单。可能您想通过支持事务的通道在线程之间进行所有通信,否则可能被设计为适合线程间通信。考虑一下“使用事务的数据库,例如postgres”或“消息队列系统,例如Rabbitmq”。

显示如何以并发方式处理此类数据:

synchronized(list) {
    if (!list.contains(a)) list.add(a);
}

假定对该同步列表的所有访问是通过同步防护完成的,则上述操作将起作用,并且永远不会导致a两次进入该列表。

或者,考虑一下您的数据结构。在这个假设的示例中,如果目标不是两次在列表中出现相同的项目,则集合会更具逻辑性。确保使用集合公开的“原始元素”:

Set<String> set = ConcurrentHashMap.newKeySet();
...
set.add(a); // safe, no need for a synchronized block

或带有地图的另一个示例:

Map<Integer, List<String>> m = new ConcurrentHashMap<>();

//unsafe:
if (!m.containsKey(1)) m.put(1, new ArrayList<String>());

// safe:
m.computeIfAbsent(1, a -> new ArrayList<String>());