首页 > 其他分享 >ArrayList线程安全问题分析

ArrayList线程安全问题分析

时间:2023-10-14 16:44:55浏览次数:36  
标签:java int ArrayList list 安全 线程 size

测试代码:

import java.util.ArrayList;

public class TestThreadSafe {
    static final int LOOP_NUM = 10;
    public static void main(String[] args) throws InterruptedException {
        ThreadSafeSubClass test = new ThreadSafeSubClass();
        test.method1(LOOP_NUM);
    }
}
class ThreadSafe {
    int i = 0;
    public final void method1(int loopNumber) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < loopNumber; i++) {
            method2(list);
            method3(list);
        }
    }
    private void method2(ArrayList<String> list) {
        list.add("1");
        System.out.println("add-"+ (i++));
    }
    public void method3(ArrayList<String> list) {
        list.remove(0);
    }
}
class ThreadSafeSubClass extends ThreadSafe{
    @Override
    public void method3(ArrayList<String> list) {
        new Thread(() -> {
            list.remove(0);
            System.out.println(Thread.currentThread().getName() + "removed--" + list.size());
        }).start();
    }
}

出现异常:

Exception in thread "Thread-9" java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
	at java.base/java.util.Objects.checkIndex(Objects.java:359)
	at java.base/java.util.ArrayList.remove(ArrayList.java:504)
	at org.timqiu.ThreadSafeSubClass.lambda$method3$0(TestThreadSafe.java:36)
	at java.base/java.lang.Thread.run(Thread.java:833)

原因分析:

既然是list.remove(0)中的Objects.checkIndex()出现了问题,我们就进源码看一看:

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;  //每一次add,size都会加1
} 
public E remove(int index) {
    // 根据异常信息,index和size都是0才会导致检查不通过,为什么呢?接着往下看
    Objects.checkIndex(index, size);	
    final Object[] es = elementData;

    @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    //这个方法修改了size
    fastRemove(es, index);

    return oldValue;
}

private void fastRemove(Object[] es, int i) {
    modCount++;
    final int newSize;
    // 注意这里
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);
    es[size = newSize] = null;
}

复现问题步骤:

1、main线程执行method1,进入for循环

2、main线程执行method2中的add方法,ArrayList中的size为1

3、main线程执行method3方法,创建一个remove线程,我们暂且称之为T1线程,但这个T1线程执行到fastRemove()方法中的System.arraycopy()就停下了,现在的size为1,所以newsize=size-1=0,此时CPU将时间片分给main线程

4、main线程进入第二轮for循环,又执行了一遍method2中的add方法,此时ArrayList中的size为2

5、T1线程恢复执行,将值为0的newsize赋给size,此时ArrayList中的size为0!!!

6、main线程循环继续执行method3,创建了一个T2线程,CPU将时间片让给T2线程执行

7、问题来了,T2线程进入remove方法,但此时的ArrayList的size是0,传进来的index也为0,Objects.checkIndex()一看,直接就报了个异常!

标签:java,int,ArrayList,list,安全,线程,size
From: https://www.cnblogs.com/timqiu/p/17764344.html

相关文章

  • 锁+多线程
     互斥锁mutex:保证共享数据操作的完整性,保证在任一时刻只能有一个线程访问对象。锁有两个操作。一个P操作(上锁),一个V操作(解锁)。P和V都是原子操作,就是在执行P和V操作时,不会被插队。锁一般使用信号量来实现的,mutex其实就是信号量=1。互斥量就是同一时间能够分给一个人,即S=1。S=......
  • 网络安全笔记DAY01
    实战:MS17-010(永恒之蓝)漏洞测试实战背景:2017年4月14日晚,黑客团体ShadowBrokers(影子经纪人)公布一大批网络攻工具,其中包含“永恒之蓝”工具,“永恒之蓝”利用Windows系统的SMB漏洞可以获取系统最高权限。恶意代码会扫描开放445文件共享端口的Windows机器,无需用户任何操作,只要开机上网......
  • 20211325 2023-2024-1 《信息安全系统设计与实现(上)》第五周学习笔记
    202113252023-2024-1《信息安全系统设计与实现(上)》第五周学习笔记一、任务要求自学教材第11章,提交学习笔记(10分),评分标准如下:1.知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容(4分)“我在学***X知......
  • openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文
    openGauss学习笔记-99openGauss数据库管理-管理数据库安全-客户端接入认证之配置文件参考99.1参数说明表1参数说明参数名称描述取值范围local表示这条记录只接受通过Unix域套接字进行的连接。没有这种类型的记录,就不允许Unix域套接字的连接。只有在从服务器本机......
  • python多线程with方式加锁
    python多线程with方式加锁"""pythonTreading中的Lock模块提供了加锁和释放锁的方法,分别是acquire()和release().这两个方法可以搭配python的with语句使用."""#示例fromthreadingimportLock​temp_lock=Lock()​withtemp_lock: print(temp_lock) #输出是<locked......
  • Java程序的main主线程的运行过程
    在Java虚拟机进程中,执行程序代码的任务是由线程来完成的。每当用java命令启动一个Java虚拟机进程,Java虚拟机就会创建并启动一个main主线程,该线程从程序入口main()方法开始执行。main主线程执行main()方法下面以例程1的Sample为例,介绍线程的运行过程。例程1 Sample.javapublicclas......
  • 并发编程-4.用户界面响应能力和线程
    利用后台线程在第一章中,我们学习了如何创建后台线程并讨论了它们的一些用途。后台线程的优先级低于进程的主线程和其他线程池线程。此外,活动的后台线程不会阻止用户或系统终止应用程序。这意味着后台线程非常适合执行以下任务:•写入日志和分析数据•监控网络或文件系统资源......
  • SimpleDateFormat线程安全性
    SimpleDateFormat线程安全性0结论SimpleDateFormat是线程不安全的。在JDK中关于SimpleDateFormat有这样一段描述:Dateformatsarenotsynchronized.Itisrecommendedtocreateseparateformatinstancesforeachthread.Ifmultiplethreadsaccessaformatconcurr......
  • 智慧矿山&矿山安全生产:AI算法实现矿山井下堆料检测
    在矿山行业中,堆料是一个重要的环节。堆料过程中,常常出现堆料不均匀、溢堆、重叠等问题,影响了生产效率和质量。传统的堆料检测方法耗时且不准确,无法满足井下作业的需求,因此需要一种智能化的堆料检测解决方案,这就是AI算法实现井下堆料检测的应用场景。AI算法技术在矿山行业中的应用日......
  • 并行编程-1.托管线程概念
    .NET线程基础知识是时候开始学习C#和.NET中的线程基础知识了。我们将介绍.NET6中可用的托管线程概念,但其中许多功能从一开始就是.NET的一部分。System.Threading命名空间从.NETFramework1.0开始可用。在随后的20年中,为开发人员添加了许多有用的功能。为了在......