线程安全的List之CopyOnWriteArrayList

系列文章目录


文章目录


前言

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。
在这里插入图片描述
ArrayList是线程不安全的,这点毋庸置疑。因为ArrayList的所有方法既没有加锁,也没有进行额外的线程安全处理。

而Vector作为线程安全版的ArrayList,存在感总是比较低。因为无论是add、remove还是get方法都加上了synchronized锁,所以效率低下。


在这里插入图片描述
无意中看到掘金中有人写了这样一遍文章(我花了两天时间没解决的问题,chatgpt用了5秒搞定)

看到最后,终归来说是一个多线程下的并发问题,有些人可能觉得这个问题很弱,但是咋说呢,我也遇上了。

因为我按正常逻辑写了一段代码,需要从数据库查询7次数据,后来发现每次查询的速度特别慢,由于业务原因SQL暂时无法优化。

此时想到一个解决方案,就是多条退走路,以下代码模拟了基本流程

package com.example.springboot;
 
import cn.hutool.core.date.DateUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
 
public class IpTest {
    public static void main(String[] args) {
        List<String> reList = new ArrayList<>();
        ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(7);
        CountDownLatch countDownLatch = new CountDownLatch(7); // 一周
        for (int i = 0; i < 7 ; i++){
            CompletableFuture.supplyAsync(() -> {
                try {
                    reList.add(DateUtil.now());
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    countDownLatch.countDown(); // 异步执行结束
                }
                return null;
            }, pool);
        }
        // 同步等待查询结果
        try {
            countDownLatch.await(30, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
        }
        reList.stream().forEach(s -> System.out.println(s));
        pool.shutdown();
    }
}

CountDownLatch用于主线程堵塞等待子任务执行完毕,参考(Future机制实际应用)

CompletableFuture用于异步执行任务,注意务必在finally中释放countDownLatch,否则主线程会一直堵塞30秒。

多次运行这个程序,你会发现一个问题,有时会打印出来空

2023-06-29 09:45:42
null
2023-06-29 09:45:42
2023-06-29 09:45:42
2023-06-29 09:45:42
2023-06-29 09:45:42

原因就是因为ArrayList不是线程安全。

解决方案有两种

(1)使用synchronized关键字

CompletableFuture.supplyAsync(() -> {
try {
synchronized (reList){
reList.add(DateUtil.now());
}
}catch (Exception e){
e.printStackTrace();
}finally {
countDownLatch.countDown(); // 异步执行结束
}
return null;
}, pool);

(2)使用CopyOnWriteArrayList代替

List<String> reList = new CopyOnWriteArrayList<>();

关于CopyOnWriteArrayList的解释,网上一大堆,这里点到为止。

这里简单说下,CopyOnWriteArrayList不管是add也好,还是remove也好。都是通过ReentrantLock + volatile + 数组拷贝来实现线程安全的。

而且每次add/remove操作都会开辟新数组,会占用系统内存。

但是存在肯定也是有好处的,就是get(int index)不需要加锁,因为CopyOnWriteArrayList在add/remove操作时,不会修改原数组,所以读操作不会存在线程安全问题。这其实就是读写分离的思想,只有写入的时候才加锁,复制副本来进行修改。CopyOnWriteArrayList也叫写时复制容器。

而且在迭代过程中,即使数组的结构被改变也不会抛出ConcurrentModificationException异常。因为迭代的始终是原数组,而所有的变化都发生在原数组的副本上。所以对于迭代器来说,迭代的集合结构不会发生改变。

总结:CopyOnWriteArrayList的优点主要有两个:

线程安全

大大的提高了“读”操作的并发度(相比于Vector)

缺点也很明显:

每次“写”操作都会开辟新的数组,浪费空间

无法保证实时性,因为“读”和“写”不在同一个数组,且“读”操作没有加互斥锁,所以不能保证强一致性,只能保证最终一致性

add/remove操作效率低,既要加锁,还要拷贝数组

所以CopyOnWriteArrayList比较适合读多写少的场景。

相关推荐

  1. CopyOnWriteArrayList线安全吗?

    2024-03-22 20:12:01       21 阅读
  2. 线安全Map

    2024-03-22 20:12:01       45 阅读
  3. ArrayList 是线安全么?

    2024-03-22 20:12:01       19 阅读
  4. 线安全单例模式

    2024-03-22 20:12:01       21 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-22 20:12:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-22 20:12:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-22 20:12:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-22 20:12:01       20 阅读

热门阅读

  1. MacOS - GCC 版本升级解决方案

    2024-03-22 20:12:01       21 阅读
  2. 蓝桥杯考试注意事项

    2024-03-22 20:12:01       24 阅读
  3. HarmonyOS状态管理:@State与@Prop、@Link的示例

    2024-03-22 20:12:01       16 阅读
  4. docker基础(七)之docker start/stop/kill/restart/pause/unpause

    2024-03-22 20:12:01       19 阅读
  5. GC垃圾回收的算法

    2024-03-22 20:12:01       21 阅读
  6. 面试宝典:MySQL 主从同步深度解析

    2024-03-22 20:12:01       20 阅读
  7. Git 常用命令总结

    2024-03-22 20:12:01       17 阅读
  8. Appium官方文档常用方法介绍

    2024-03-22 20:12:01       20 阅读
  9. C++ 虚函数和多态

    2024-03-22 20:12:01       19 阅读
  10. MPI4.1文档3-MPI过程与MPI数据类型

    2024-03-22 20:12:01       16 阅读
  11. 1064:奥运奖牌计数

    2024-03-22 20:12:01       20 阅读