提问者:小点点

并发程序中丢失的列表元素[重复]


这是《头先Java》一书中的一个任务,要求读者找出以下代码块中发生了什么:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TwoThreadsWriting {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        Data data = new Data();
        threadPool.execute(()-> addLetterToData('a', data));
        threadPool.execute(()-> addLetterToData('A', data));
        threadPool.shutdown();
    }

    private static void addLetterToData(char letter, Data data) {
        for(int i = 0; i < 26; i++){
            data.addLetter(letter++);
            try {
                Thread.sleep(50);
            } catch (InterruptedException ignored) {}
        }
        System.out.println(Thread.currentThread().getName() + data.getLetters());
        System.out.println(Thread.currentThread().getName() + " size: " + data.getLetters().size());
    }
}
class Data {
    private final List<String> letters = new ArrayList<>();
    public List<String> getLetters() {return letters;}
    public void addLetter(char letter){
        letters.add(String.valueOf(letter));
    }
}

我期望输出列表包含52个这样的字符

['a', 'A', 'b', 'B', ...]

但有时元素数小于52,字母中缺少一些字符,例如这里('K'缺失):

pool-1-thread-2[A, a, B, b, c, C, d, D, e, E, f, F, G, g, H, h, i, I, J, j, k, L, l, m, M, N, n, O, o, p, P, q, Q, r, R, s, S, t, T, u, U, V, v, w, W, X, x, y, Y, Z, z]

pool-1-thread-1[A, a, B, b, c, C, d, D, e, E, f, F, G, g, H, h, i, I, J, j, k, L, l, m, M, N, n, O, o, p, P, q, Q, r, R, s, S, t, T, u, U, V, v, w, W, X, x, y, Y, Z, z]

pool-1-thread-2 size: 51
pool-1-thread-1 size: 51

我不明白为什么会发生这种情况。我尝试通过打印addLet的局部变量字母的值来调试它,但它只向我显示实际上添加了'K'

我知道这一切的发生仅仅是因为使用了非线程安全的ArrayListaddMail没有被声明为同步,但我想首先了解这里发生了什么。


共1个答案

匿名用户

ArrayList不是线程安全的。如果两个线程试图同时调用add(),任何事情都可能发生。从Arraylist留档:

请注意,此实现不是同步的。如果多个线程并发访问一个ArrayList实例,并且其中至少有一个线程在结构上修改了列表,则必须在外部同步。