为什么使用线程添加值后从 ArrayList 获取空值

Ami*_*arg 5 java collections

我多次运行以下代码,发现有时我从 ArrayList 中得到“null”。当我向数组添加整数值时,我无法理解为什么会发生这种情况。

package com;

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

public class test implements Runnable {

    static List<Integer> ls = new ArrayList<Integer>();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new test());
        Thread t2 = new Thread(new test());

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(ls.size());
        for (int i = 0; i < ls.size(); ++i) {
            System.out.println(i + "  " + ls.get(i));
        }
    }

    @Override
    public synchronized void run() {
        try {
            for (int i = 0; i < 20; ++i) {
                ls.add(i);
                Thread.sleep(5);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

36  
0  0  
1  0  
2  1  
3  1  
4  2  
5  2  
6  3  
7  3  
8  4  
9  4  
**10  null**   
11  5   
12  6   
13  6  
14  7   
15  7   
16  8  
17  8  
18  9  
19  9  
20  10  
21  10  
22  11  
23  11  
24  12  
25  12  
26  13  
27  14  
28  15  
29  16  
30  16  
31  17  
32  17  
33  18  
34  19   
35  19  
Run Code Online (Sandbox Code Playgroud)

我知道为什么总大小是 36。但我想知道为什么我在第 10 位得到空值。

注意:这并不是每次都会生成。您可能需要运行此代码 5 到 10 次才能生成此代码。

Mik*_*son 3

您遇到的情况称为竞争条件。

“竞赛”发生在实现您正在使用的集合对象的Java 运行时库代码中。(而且,到目前为止,你非常幸运,它没有让你的整个程序崩溃。下次,它很容易......情况不稳定

如果您打算在线程中使用容器类,则必须确保您使用的类是“线程安全的”,这意味着它们包含必要的逻辑,以允许它们同时被多个线程正确使用。 或者,您必须在整个应用程序中手动实现适当的互斥逻辑。ls.get()(主线程必须与子线程互锁,以便在线程同时执行操作时它不会尝试ls.add()。)

如果您使用的类不是线程安全的,有时您会看到应用程序定义了自己设计的“包装类”,该类在其互斥逻辑中“包装”对非线程安全类的调用自己的设计,以便包装器“是”线程安全的,至少对于应用程序来说是这样。

(PS:这个原则适用于任何编程语言。)