我们在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的大小小于零怎么可能?如果该列表不是线程安全的,可能是并发问题吗?无论如何我们都无法复制它。
答案 0 :(得分:4)
该异常跟踪中的“ -1”是由outOfBoundsMsg
私有方法产生的,该方法只将字段size
放在其中。
在相关方法中,通过size
和size++
修改了size--
字段。
留下以下解释:
这是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>());