Collections.synchronizedList是如何将List变为线程安全的

一、synchronizedList 的线程安全实现逻辑

  1. 包装类选择
    调用 Collections.synchronizedList(List<T> list) 时,会根据传入 List 是否实现 RandomAccess 接口(如 ArrayList 实现该接口,LinkedList 不实现),返回不同的包装类:
  • 实现 RandomAccess:返回 SynchronizedRandomAccessList(继承自 SynchronizedList);
  • 未实现:返回 SynchronizedList
java 复制代码
    public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }
  1. 核心锁机制
    线程安全的核心是 对象锁(mutex)+ synchronized 同步块
  • SynchronizedList 继承自 SynchronizedCollection,在父类中初始化 mutex 锁对象(mutex = this,即包装类实例本身);
  • getsetaddremove 等所有修改/查询方法,均通过 synchronized (mutex) 包裹,确保同一时刻只有一个线程能执行这些方法。
java 复制代码
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 3053995032091335093L;

        final Collection<E> c;  // Backing Collection
        final Object mutex;     // Object on which to synchronize

        SynchronizedCollection(Collection<E> c) {
            this.c = Objects.requireNonNull(c);
            mutex = this;
        }

        SynchronizedCollection(Collection<E> c, Object mutex) {
            this.c = Objects.requireNonNull(c);
            this.mutex = Objects.requireNonNull(mutex);
        }

        public int size() {
            synchronized (mutex) {return c.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return c.isEmpty();}
        }
        public boolean contains(Object o) {
            synchronized (mutex) {return c.contains(o);}
        }
        public Object[] toArray() {
            synchronized (mutex) {return c.toArray();}
        }
        public <T> T[] toArray(T[] a) {
            synchronized (mutex) {return c.toArray(a);}
        }

        public Iterator<E> iterator() {
            return c.iterator(); // Must be manually synched by user!
        }

        public boolean add(E e) {
            synchronized (mutex) {return c.add(e);}
        }
        public boolean remove(Object o) {
            synchronized (mutex) {return c.remove(o);}
        }

        public boolean containsAll(Collection<?> coll) {
            synchronized (mutex) {return c.containsAll(coll);}
        }
        public boolean addAll(Collection<? extends E> coll) {
            synchronized (mutex) {return c.addAll(coll);}
        }
        public boolean removeAll(Collection<?> coll) {
            synchronized (mutex) {return c.removeAll(coll);}
        }
        public boolean retainAll(Collection<?> coll) {
            synchronized (mutex) {return c.retainAll(coll);}
        }
        public void clear() {
            synchronized (mutex) {c.clear();}
        }
        public String toString() {
            synchronized (mutex) {return c.toString();}
        }
        // Override default methods in Collection
        @Override
        public void forEach(Consumer<? super E> consumer) {
            synchronized (mutex) {c.forEach(consumer);}
        }
        @Override
        public boolean removeIf(Predicate<? super E> filter) {
            synchronized (mutex) {return c.removeIf(filter);}
        }
        @Override
        public Spliterator<E> spliterator() {
            return c.spliterator(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> stream() {
            return c.stream(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> parallelStream() {
            return c.parallelStream(); // Must be manually synched by user!
        }
        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }

二、效率低下的原因

SynchronizedList 采用 "粗粒度锁" 实现:

所有方法共用同一把 mutex 锁,无论操作是读(如 get)还是写(如 add),都会独占锁。这意味着即使多个线程仅执行读操作,也需要排队等待锁释放,无法实现"读-读并发",导致并发效率显著低于 CopyOnWriteArrayList 等细粒度锁/无锁实现。

三、遍历需额外加锁的原因

SynchronizedList 内部的锁仅保证 单个方法调用的原子性 ,但无法保证 多步操作(如遍历)的原子性

  • iterator() 方法直接返回底层 List 的迭代器(未加锁),迭代器本身不具备线程安全性;
  • 若不加外部锁,可能出现"并发修改异常"(如线程 A 遍历 hasNext() 时存在元素,线程 B 立即删除该元素,线程 A 调用 next() 时会报错)。

因此,官方要求遍历 SynchronizedList 时,需在外部用 synchronized (list) 包裹迭代逻辑,确保遍历全程独占锁,避免并发修改问题。

java 复制代码
 public Iterator<E> iterator() {
     return c.iterator(); // Must be manually synched by user!
 }

示例代码如下:

java 复制代码
List list = Collections.synchronizedList(new ArrayList());
// 正确遍历方式
synchronized (list) {
    Iterator i = list.iterator(); 
    while (i.hasNext()) {
        foo(i.next());
    }
}
相关推荐
nbwenren15 分钟前
node.js内置模块之---crypto 模块
java
三道渊44 分钟前
C语言:文件I/O
c语言·开发语言·数据结构·c++
weyyhdke1 小时前
springboot和springframework版本依赖关系
java·spring boot·后端
kali-Myon1 小时前
CTFshow-Pwn142-Off-by-One(堆块重叠)
c语言·数据结构·安全·gdb·pwn·ctf·
chools2 小时前
Java后端拥抱AI开发之个人学习路线 - - Spring AI【第一期】
java·人工智能·学习·spring·ai
计算机安禾2 小时前
【数据结构与算法】第19篇:树与二叉树的基础概念
c语言·开发语言·数据结构·c++·算法·visual studio code·visual studio
jeCA EURG2 小时前
Spring Boot 2.7.x 至 2.7.18 及更旧的版本,漏洞说明
java·spring boot·后端
BduL OWED3 小时前
Redis之Redis事务
java·数据库·redis
寻寻觅觅☆3 小时前
东华OJ-基础题-30-求最晚和最早日期(C++)
数据结构·c++·算法
FastBean3 小时前
BizAssert:一个轻量级、生产就绪的 Java 业务断言工具类
java·后端