Java 多线程行为解释

标签 java multithreading indexoutofboundsexception

我正在学习Java多线程。我写了一小段代码并产生了一些我无法理解的输出..请帮助解释一下。 发布下面的代码。

package com.java.learn;

import java.util.ArrayList;
import java.util.List;

public class ListTestWithMultiThread {
    static final List<Integer> list = new ArrayList<Integer>();

    public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    list.add(Integer.valueOf(i));
                }
                System.out.println("List size at thread 0 : " + list.size());
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 101; i <= 200; i++) {
                    list.add(Integer.valueOf(i));
                }
                System.out.println("List size at thread 1 : " + list.size());
            }
        }).start();
    }
}

各种运行中的一些o/p: 线程 0 处的列表大小:134 线程 1 处的列表大小:200

Exception in thread "Thread-1" List size at thread 0 : 101
java.lang.ArrayIndexOutOfBoundsException: 17
    at java.util.ArrayList.add(Unknown Source)
    at com.java.learn.ListTestWithMultiThread$2.run(ListTestWithMultiThread.java:25)
    at java.lang.Thread.run(Unknown Source)

    List size at thread 0 : 106
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 58
    at java.util.ArrayList.add(Unknown Source)
    at com.java.learn.ListTestWithMultiThread$2.run(ListTestWithMultiThread.java:25)
    at java.lang.Thread.run(Unknown Source)

最佳答案

您正在访问的数据结构( list )不是为并行并发访问而设计的,并且没有对其进行保护(例如通过 synchronized )。这最终会破坏数据结构的内部结构,导致奇怪的行为,比如你得到的异常。

这里有两种方法来保护它:

  1. 使用并发数据结构:

List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());

  • 使用synchronized保护列表:

    synchronized(list) {
          list.add(Integer.valueOf(i));
    }
    
  • 编辑:既然您要求这样做,那么 ArrayList 的方式如下:可能会被损坏。 安ArrayList由一个数组支持,当列表增长时,该数组必须调整大小。这里调整大小意味着分配一个更大的新数组,并将旧数组的内容复制到新数组。以下是 the code 的部分内容这样做是这样的:

    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    ...
    elementData = Arrays.copyOf(elementData, newCapacity);
    

    现在想象一下:线程 A 开始调整数组大小,计算新容量并开始复制第 4 行中的数据。但在它可以将新数组的引用复制到 elementData 之前无论出于何种原因,它都会停止(这种情况经常发生)。现在线程 B 开始调整数组大小并完成。然后它会向列表中插入更多值并再次调整数组大小并完成。线程 B 现在假设该列表对于新值来说足够大,但在插入该值之前,线程 A 唤醒并覆盖 elementData以及对其创建的较小数组的引用。线程 B 现在尝试将值插入到较小的数组中并获得 ArrayIndexOutOfBoundsException 。这一切都不太可能发生,但正如您所见,它可能会发生。

    关于Java 多线程行为解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35519352/

    相关文章:

    java - 二维数组中的联合查找 (Java)

    c++ - 将全局引用计数资源与原子同步——在这里放宽合适吗?

    python - Gtk python 中的线程

    android - java.lang.IndexOutOfBoundsException : Index: 2, 大小:微调项选择中的 2

    java - 使用变量副本时索引超出范围

    java - 使用预制模型文件预测在 WEKA 中动态创建的数据

    java - 如何返回 DocumentSnapShot 作为方法的结果?

    java - Spring Boot中跨子域共享cookie

    java - 按 yyyymmdd 整数计算日差时结果不一致

    java - 跳到单线程 ExecutorService 中的下一个任务?