JUC ArrayList为什么不是线程安全的&如何安全

728 阅读2分钟

这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

引言

最近在学习群里看到群友们在讨论一个问题:并发的向ArrayList里添加10000个元素元素,最后打印出ArrayList的size(),打印结果小于10000,便在群里问这事怎么回事?

首先我们针对三个小伙伴的代码挨个来点评下:

代码检视

第一位

971637857636_.pic.jpg 首先,这个小伙伴饭了一个非常恐怖的错误, for循环里面创建了一万个线程,这对程序来说无疑是一个灾难。虽然这里是一个单机的Demo程序,对于资深的程序员来说,有些东西是要刻在DNA里面的。虽然Java程序员不能像C++程序员对每一块内存都如数家珍,但也不能这么暴殄天物。

其次,这位同学不知道在哪里学到的这个等待线程全部执行完成的方法,while(Thread.activeCount() > 2)很神奇,他还解释说,如果是IDEA这里写2,如果用Eclipse这里写1。说实话还有点可爱。(说明:此方法仅能在运行单机Demo时测试用,在生产环境实不可取的。)好的地方是他考虑到了主线程会早于new出来的线程结束,所以让CPU空转来等待其他线程完成。

说完了编码上的硬伤,我们来看一下这位同学对并发安全的理解:没有理解。他完全没有做任何线程安全的措施,所以输出的结果 9990 是不符合预期的,好吧,我们来看下一位。

第二位

image.png

image.png

在得到其他童鞋的指点后,第二位同学马上做出了调整

第二位的第二次尝试

image.png

image.png

第二位同学放弃了。

ArrayList的线程安全性

大家都知道ArrayList不是线程安全的,那他究竟是怎么不安全了。如何让他变得安全,今天我们就来看看。

啪,很快啊,我就写出了一串代码

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * ArrayList线程安全实践
 */
public class ArrayListSecturyTest {

    public static void main(String[] args) throws InterruptedException {
        final List<Integer> list = new ArrayList();
        int num = 10000;

        ExecutorService e = new ThreadPoolExecutor(10, 10, 5L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(num));

        final CountDownLatch countSign = new CountDownLatch(num);
        for (int i = 0; i < num; i++) {
            final int finalI = i;
            e.execute(new Runnable() {
                public void run() {
                    synchronized (list) {
                        list.add(finalI);
                        countSign.countDown();
                    }
                }
            });
        }
        countSign.await();
        e.shutdown();
        System.out.println(list.size());
    }

}

简单说下思路:

// todo

创建一个线程池,然后使用

完整代码: gitee.com/StephenRead…

执行结果:

不安全的版本 用时:4370 ms result: 9984520

安全的版本 - 使用synchronize 用时:3410 ms result: 10000000

安全的版本 - 使用ReentrantLock 用时:6688 ms result: 10000000

安全的版本 - 使用Collections.synchronizedList(new ArrayList<>()) 用时:3033 ms result: 10000000

Process finished with exit code 0