List线程不安全解决办法和适用场景

List线程不安全解决办法和适用场景

ArrayList 多线程下的线程不安全问题

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class ListThreadUnsafeDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // 10个线程同时向List添加元素
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    list.add(Thread.currentThread().getName() + "-" + j);
                }
            }).start();
        }

        // 等待所有线程执行完成(简单休眠,实际开发用CountDownLatch)
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 预期size=1000,实际大概率小于1000(数据丢失),或直接抛异常
        System.out.println("List最终长度:" + list.size());
    }
}

问题原因 :ArrayList底层是数组,写操作(add等)无锁保护,多线程同时修改数组大小、元素索引时,会出现操作覆盖 、索引错位,最终导致数据丢失或异常。

List 线程不安全的 2 种核心解决方案

使用CopyOnWriteArrayList(读多写少场景)

读多写少场景的最优解 ,核心原理是写时复制(Copy On Write):

  • 读操作:无锁,直接访问底层数组,多线程同时读不阻塞、高性能;
  • 写操作(add/remove/set):先加锁,再复制一份全新的底层数组,在新数组上执行修改,修改完成后将底层数组引用指向新数组,最后释放锁;
  • 遍历操作:基于原数组遍历,即使遍历中其他线程修改 List,也不会抛ConcurrentModificationException。
java 复制代码
// 仅替换为CopyOnWriteArrayList,其余代码不变
List<String> list = new CopyOnWriteArrayList<>();

手动加锁保护普通 ArrayList(灵活可控,写多读少场景推荐)

核心是将多线程的非原子操作包裹在锁范围内,保证操作的原子性。

java 复制代码
for (int j = 0; j < 100; j++) {
                    // 核心:将写操作包裹在synchronized代码块中,锁对象为list本身
     synchronized (list) {
   		 list.add(Thread.currentThread().getName() + "-" + j);
 		}
}

使用ReentrantLock可重入锁(灵活,支持公平锁 / 非公平锁,推荐复杂场景)

比synchronized更灵活,支持手动加锁 / 释放锁 、公平锁 (按线程等待顺序获取锁)、尝试获取锁 (tryLock()),适合需要精细控制锁的场景:

java 复制代码
private static final List<String> list = new ArrayList<>();
    // 定义可重入锁(默认非公平锁,传true为公平锁)
    private static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    lock.lock(); // 加锁
                    try {
                        // 核心操作:在锁保护下执行写操作
                        list.add(Thread.currentThread().getName() + "-" + j);
                    } finally {
                        lock.unlock(); // 释放锁(必须在finally中,防止异常导致锁泄漏)
                    }
                }
            }).start();
        }

拓展:复合操作的线程安全

复合操作指「多个 List 操作组合成的逻辑」(如判断是否为空 → 删除元素、判断是否存在 → 修改元素),这类操作即使使用线程安全 List,也需要额外加锁,否则会出现线程安全问题。

java 复制代码
    private static final List<String> list = new CopyOnWriteArrayList<>();    
	public static void removeFirst() {
        // 复合操作:isEmpty() + remove(0),无锁保护,线程不安全
        if (!list.isEmpty()) {
            list.remove(0);
        }
    }
    public static void main(String[] args) {
        // 先添加元素
        list.add("A");
        list.add("B");

        // 2个线程同时执行removeFirst()
        new Thread(ListComplexOperationUnsafe::removeFirst).start();
        new Thread(ListComplexOperationUnsafe::removeFirst).start();
    }

问题 :两个线程可能同时通过isEmpty()判断,然后同时执行remove(0),导致索引越界异常。

java 复制代码
public static void removeFirst() {
        lock.lock();
        try {
            // 复合操作包裹在锁中,保证原子性
            if (!list.isEmpty()) {
                list.remove(0);
            }
        } finally {
            lock.unlock();
        }
    }
相关推荐
code2cat4 分钟前
【随笔】RAG语义缓存:问题很相似,答案就能直接复用吗
java·缓存·知识库·rag
爱吃火鸡面呀14 分钟前
Doris 数据划分实战:List 分区、分桶列与桶数选择
数据结构·list
Rain的Java大神之路26 分钟前
面了 50 个候选人,90% 都挂在 LeetCode 3 无重复字符的最长子串:滑动窗口的 5 个致命坑
java·后端·leetcode
西凉的悲伤27 分钟前
Jsoup 库 HTML 解析详解
java·前端·html·jsoup
RuoyiOffice31 分钟前
SpringBoot OA推荐:面向信创选型,看看这套企业协同平台
spring boot·vue3·信创·flowable·企业协同·ruoyi office·oa选型
十碗饭吃不饱39 分钟前
Apache POI 踩坑:setFontFamily 二次设置字体不生效、样式残留问题深度解析
java·word·apache
jianxuan41 分钟前
Spring Boot + Maven 接入 Allatori:从普通打包到可选混淆发布
spring boot·python·maven
我命由我1234543 分钟前
6-2.Python 异常处理 - 异常捕获(捕获指定异常、常见异常类型、异常信息)
java·运维·开发语言·python·http·https·java-ee
Flynn Liang1 小时前
保姆级|Spring AI 2.0 第一个 Demo:Spring Boot 4 接入大模型(含国内可用方案 + 完整 pom)
人工智能·spring·ollama·springai·deepseek·springai入门·java接入大模型
zaizai10071 小时前
STL深度剖析2
java·c++·算法