java中arraylist线程安全吗_(Java) 如何验证 ArrayList 不是线程安全的

思路

1d87565d53ac

ArrayList 中的 add(E e) 方法

java.util.ArrayList 中的 add(E e) 方法里有 size++ 的操作,而 size++ 并不是原子操作,所以如果在 size++ 的过程中,其他线程被调度,并且也去操作同一个 ArrayList,那么 size 的值就可能不准确了。

实战

有了这样的思路,我们就可以让一些线程在同一个 ArrayList 上调用 add(E e) 方法。我写了两段示例代码来进行验证。

首先是让 100 个线程对同一个 ArrayList 执行 add(E e) 方法。

并发地对 ArrayList 进行添加元素的操作

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Main {

public static void main(String[] args) throws Exception {

// 让 cnt 个线程并发地对 list 进行添加元素的操作

final int cnt = 100;

CountDownLatch countDownLatch = new CountDownLatch(cnt);

ExecutorService executorService = Executors.newCachedThreadPool();

final int n = 100;

// 令 initialCapacity=cnt * n, 容量肯定够大了, 程序运行过程中就不会出现扩容

List list = new ArrayList<>(cnt * n);

Runnable runnable = () -> {

for (int i = 0; i < n; i++) {

// 添加元素

list.add(i);

// 尝试让其他线程执行, 让问题更容易暴露出来

Thread.yield();

}

countDownLatch.countDown();

};

for (int i = 0; i < cnt; i++) {

executorService.execute(runnable);

}

executorService.shutdown();

countDownLatch.await();

System.out.println(list.size());

}

}

一次运行结果如下

1d87565d53ac

最终的 size 可能小于10000

并发地对 Vector 进行添加元素的操作

java.util.Vector 是线程安全的,所以如果我们用一些线程并发地对 Vector 进行添加元素的操作,最终的 size 应该不会有问题。

将上面程序里的 ArrayList 改为 Vector,程序会变为

import java.util.ArrayList;

import java.util.List;

import java.util.Vector;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Main {

public static void main(String[] args) throws Exception {

// 让 cnt 个线程并发地对 list 进行添加元素的操作

final int cnt = 100;

CountDownLatch countDownLatch = new CountDownLatch(cnt);

ExecutorService executorService = Executors.newCachedThreadPool();

final int n = 100;

// 令 initialCapacity=cnt * n, 容量肯定够大了, 程序运行过程中就不会出现扩容

List list = new Vector<>(cnt * n);

Runnable runnable = () -> {

for (int i = 0; i < n; i++) {

// 添加元素

list.add(i);

// 尝试让其他线程执行, 让问题更容易暴露出来

Thread.yield();

}

countDownLatch.countDown();

};

for (int i = 0; i < cnt; i++) {

executorService.execute(runnable);

}

executorService.shutdown();

countDownLatch.await();

System.out.println(list.size());

}

}

一次运行结果如下

1d87565d53ac

最终的 size 是 10000

为什么并发地对 Vector 执行 add(E e) 操作就没有出现 size 不对的问题呢?

1d87565d53ac

image.png

上图是 Vector 源码中的 add(E e) 部分,可见 add(E e) 是一个同步方法,所以 Vector 中的add(E e) 方法相当于一个原子操作。对同一个 Vector 而言,只有一个线程执行完 add(E e) 方法后,其他线程才有可能再次执行 add(E e) 方法。这样 elementCount 的值就不会在 add(E e) 执行期间被弄错。Vector 的 size() 方法返回的就是 elementCount 的值

1d87565d53ac

Vector 的 size() 方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值