ArrayList 和 CopyOnWriteArrayList 线程安全测试

本文通过性能测试对比了ArrayList与CopyOnWriteArrayList在并发场景下的表现,发现CopyOnWriteArrayList在读操作时无锁,适合作为读多写少的并发场景中的选择。详细分析了两种集合的线程安全性问题,提供了测试代码和结果分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayList 是 非线程安全的, CopyOnWriteArrayList 是一个线程安全,并且在读操作时无锁的ArrayList,且适合并发访问。对于集合元素数为10000,线程数量为100的情况下进行性能测试,随着元素数量和线程数量的增加,CopyOnWriteArrayList在增加元素和删除元素时的性能下降非常明显,并且性能会比ArrayList低。但在查找元素这点上随着线程数的增长,性能较ArrayList会好很多。

 

故在读多写少的并发场景中,CopyOnWriteArrayList较之ArrayList是更好的选择。

 

 

下面对两种方式的线程安全进行下测试:

 

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class ThreadSafeDemo {
    public static int demo(final List list, final int testCount) throws InterruptedException {
        ThreadGroup group = new ThreadGroup(list.getClass().getName() + "@" + list.hashCode()); 
        final Random rand = new Random(); 
        
        Runnable listAppender = new Runnable() {  // 这里面实现了Runnable接口类,覆盖了它的run方法
            public void run() {
                try {
                    Thread.sleep(rand.nextInt(2));
                } catch (InterruptedException e) {
                    return; 
                } 
                list.add("0"); 
            }
        }; 
        
        for (int i = 0; i < testCount; i++) {
            new Thread(group, listAppender, "InsertList-" + i).start(); 
                             //java.lang.Thread
.Thread(ThreadGroup
 group, Runnable
 target, String
 name)
        
      }
        
        while (group.activeCount() > 0) {
            Thread.sleep(10); 
        }
        
        return list.size(); 
    }
    public static void main(String[] args) throws InterruptedException {
        List unsafeList = new ArrayList(); 
        List safeList = Collections.synchronizedList(new ArrayList()); // 也可以换成new CopyToWriteArrayList
        final int N = 10000; 
        for (int i = 0; i < 10; i++) {
            unsafeList.clear(); 
            safeList.clear(); 
            int unsafeSize = demo(unsafeList, N); 
            int safeSize = demo(safeList, N); 
            System.out.println("unsafe/safe: " + unsafeSize + "/" + safeSize); 
        }
    }
}

 

测试的结果为:

unsafe/safe: 9992/10000
unsafe/safe: 9996/10000
unsafe/safe: 9990/10000
unsafe/safe: 9992/10000
unsafe/safe: 9997/10000
unsafe/safe: 9997/10000
unsafe/safe: 9993/10000
unsafe/safe: 9995/10000
unsafe/safe: 9993/10000
unsafe/safe: 9995/10000

 

之所以会造成不安全线程list数不足10000,主要有两点:

首先我们来看ArrayList的源码:

 

public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

 

主要问题出在size++这块,因为该方法不是线程安全的,所以就有可能出现以下两种情况:

1)线程A和线程B同样取得size=10,然后在相同位置10插入了两遍值,然后在都执行size++,结果size变成了12,这样下次再有线程进来时会在12的位置继续插入值,而11则变成了null,实验下果然如此,但是其实这并不能解释为什么list1.size的值会减少,只能解释为什么list中有的值为null

2) size++这个操作同样不是线程安全的,它分成两个步骤,第一,取size的位置,第二,size位置+1,这样就有可能A线程和B线程同时取到size的位置,然后+1,这样A,B线程执行完size++后,size的值为11而不是12,所以就会有不同的线程同时在一个位置赋值,导致list的数量不足。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值