Young Kbt blog Young Kbt blog
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)

Shp Liu

朝圣的使徒,正在走向编程的至高殿堂!
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)
  • Java 基础 - SE

  • Java 进阶 - SE

  • Java 集合 - Collection

    • Java 集合 - 集合概序
    • Java 集合 - ArrayList
    • Java 集合 - LinkedList
    • Java 集合 - Vector 和 Stack
    • Java 集合 - List 总结
      • arrayList和LinkedList区别
      • ArrayList和Vector的区别
      • 为什么现在都不提倡使用Vector了
    • Java 集合 - HashMap
    • Java 集合 - Set
    • Java 集合 - HashSet
    • Java 集合 - TreeSet
    • Java 集合 - 迭代器
    • Java 集合 - 泛型
    • Java 集合 - Collections 工具类
  • Java 并发 - JUC

  • Java 容器 - Web

  • Java 底层 - JVM

  • Java 版本 - 新特性

  • Java
  • Java 集合 - Collection
Young Kbt
2021-10-22
目录

Java 集合 - List 总结

  • arrayList和LinkedList区别
  • ArrayList和Vector的区别
  • 为什么现在都不提倡使用Vector了

# arrayList和LinkedList区别

  • ArrayList 底层是用数组实现的顺序表,是随机存取类型,可自动扩增,并且在初始化时,数组的长度是 0,只有在增加元素时,长度才会增加。默认是 10,不能无限扩增,有上限,在查询操作的时候性能更好
  • LinkedList 底层是用链表来实现的,是一个双向链表,注意这里不是双向循环链表,顺序存取类型。在源码中,似乎没有元素个数的限制。应该能无限增加下去,直到内存满了在进行删除,增加操作时性能更好

两个都是线程不安全的,在 iterator 时,会发生 fail-fast:快速失效。

# ArrayList和Vector的区别

  • ArrayList 线程不安全,在用 iterator,会发生 fail-fast
  • Vector 线程安全,因为在方法前加了 Synchronized 关键字。也会发生 fail-fast

fail-fast 和 fail-safe 区别和什么情况下会发生

简单的来说:在 java.util 下的集合都是发生 fail-fast,而在 java.util.concurrent 下的发生的都是 fail-safe。

fail-fast

快速失败,例如在 ArrayList 中使用迭代器遍历时,有另外的线程对 ArrayList 的存储数组进行了改变,比如 add、delete、等使之发生了结构上的改变,所以 Iterator 就会快速报一个 java.util.ConcurrentModificationException 异常(并发修改异常),这就是快速失败。

fail-safe

安全失败,在 java.util.concurrent 下的类,都是线程安全的类,他们在迭代的过程中,如果有线程进行结构的改变,不会报异常,而是正常遍历,这就是安全失败。

为什么在 java.util.concurrent 包下对集合有结构的改变,却不会报异常?

在 concurrent 下的集合类增加元素的时候使用 Arrays.copyOf() 来拷贝副本,在副本上增加元素,如果有其他线程在此改变了集合的结构,那也是在副本上的改变,而不是影响到原集合,迭代器还是照常遍历,遍历完之后,改变原引用指向副本,所以总的一句话就是如果在此包下的类进行增加删除,就会出现一个副本。所以能防止 fail-fast,这种机制并不会出错,所以我们叫这种现象为 fail-safe。

Vector 也是线程安全的,为什么是 fail-fast 呢?

这里搞清楚一个问题,并不是说线程安全的集合就不会报 fail-fast,而是报 fail-safe,你得搞清楚前面所说答案的原理,出现 fail-safe 是因为他们在实现增删的底层机制不一样,就像上面说的,会有一个副本,而像 ArrayList、LinekdList、Verctor 等,他们底层就是对着真正的引用进行操作,所以才会发生异常。

既然是线程安全的,为什么在迭代的时候,还会有别的线程来改变其集合的结构呢(也就是对其删除和增加等操作)?

首先,我们迭代的时候,根本就没用到集合中的删除、增加,查询的操作,就拿 Vector 来说,我们都没有用那些加锁的方法,也就是方法锁放在那没人拿,在迭代的过程中,有人拿了那把锁,我们也没有办法,因为那把锁就放在那边。

举例说明 fail-fast 和 fail-safe 的区别

  • fail-fast

image-20220618214957401

  • fail-safe

通过 CopyOnWriteArrayList 这个类来做实验,不用管这个类的作用,但是他确实没有报异常,并且还通过第二次打印,来验证了上面我们说创建了副本的事情。

原理是在添加操作时会创建副本,在副本上进行添加操作,等迭代器遍历结束后,会将原引用改为副本引用,所以我们在创建了一个 List 的迭代器,结果打印的就是 123444 了,

证明了确实改变成为了副本引用,后面为什么是三个 4,原因是我们循环了 3 次,不就添加了 3 个 4 吗。如果还感觉不爽的话,看下 add 的源码。

image-20220618215005032

image-20220618215011372

# 为什么现在都不提倡使用Vector了

  • Vector 实现线程安全的方法是在每个操作方法上加锁,这些锁并不是必须要的,在实际开发中,一般都是通过锁一系列的操作来实现线程安全,也就是说将需要同步的资源放一起加锁来保证线程安全
  • 如果多个 Thread 并发执行一个已经加锁的方法,但是在该方法中,又有 Vector 的存在,Vector 本身实现中已经加锁了,那么相当于锁上又加锁,会造成额外的开销
  • 就如上面第三个问题所说的,Vector 还有 fail-fast 的问题,也就是说它也无法保证遍历安全,在遍历时又得额外加锁,又是额外的开销,还不如直接用 ArrayList,然后再加锁呢

总结:Vector 在不需要进行线程安全的时候,也会加锁,也就导致了额外开销,所以在 JDK1.5 之后就被弃用了,现在如果要用到线程安全的集合,都是从 java.util.concurrent 包下去拿相应的类。

编辑此页 (opens new window)
更新时间: 2024/11/02, 18:07:00
Java 集合 - Vector 和 Stack
Java 集合 - HashMap

← Java 集合 - Vector 和 Stack Java 集合 - HashMap→

最近更新
01
技术随笔 - Element Plus 修改包名 原创
11-02
02
Reactor - 扩展性
11-02
03
Reactor - 最佳实践
11-02
更多文章>
Theme by Vdoing | Copyright © 2021-2024 Young Kbt | blog
桂ICP备2021009994号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式