Java 同步集合

Java 同步集合

同步集合的创建方式

通过 Collections 工具类提供的静态方法,可将普通集合转换为同步集合,保证多线程环境下单个方法操作的线程安全。

部分静态方法

  1. synchronizedList(List<E> list):将普通 List(如 ArrayList)转换为同步 List
  2. synchronizedSet(Set<E> set):将普通 Set(如 HashSet)转换为同步 Set
  3. synchronizedMap(Map<K,V> map):将普通 Map(如 HashMap)转换为同步 Map
  4. synchronizedSortedSet(SortedSet<E> sortedSet):将普通 SortedSet(如 TreeSet)转换为同步 SortedSet
  5. synchronizedSortedMap(SortedMap<K,V> sortedMap):将普通 SortedMap(如 TreeMap)转换为同步 SortedMap

代码示例

package com.collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Jing61
*/
public class SynchronizedCollection {
    private static List<String> list = new ArrayList<>();

    public static void main(String[] args) {
        /*
            使用 Collections 提供的静态方法可以将集合换成同步的集合
            1. synchronizedList(List<E> list)
            2. synchronizedSet(Set<E> set)
            3. synchronizedMap(Map<K,V> map)
            4. synchronizedSortedSet(SortedSet<E> sortedSet)
            5. synchronizedSortedMap(SortedMap<K,V> sortedMap)
         */
        list = Collections.synchronizedList(list);
        
    }
}

集合的“快速失败(Fail-Fast)”机制

快速失败的定义

“快速失败”是集合(以及多数普通集合)的一种错误检测机制,当多个线程并发操作集合,且至少有一个线程在修改集合结构(如添加、删除、清空元素) 时,迭代器会立即检测到这种并发修改行为,并抛出 java.util.ConcurrentModificationException 异常,终止迭代操作,避免出现更复杂的数据不一致或集合结构损坏问题。

快速失败的实现原理

集合的迭代器(如通过 iterator() 方法获取的迭代器)内部维护了一个 “修改计数器(modCount)”,核心逻辑如下:

  1. 迭代器创建时,会记录当前集合的 modCount 值(记为 expectedModCount)。
  2. 迭代过程中(如调用 next() 方法),迭代器会对比 currentModCount(集合当前的修改次数)与 expectedModCount
    • 若两者相等,说明迭代期间集合未被修改,继续迭代;
    • 若两者不相等,说明迭代期间有其他线程修改了集合结构,立即抛出 ConcurrentModificationException
  3. 当线程执行 add()remove() 等结构性修改操作时,集合的 modCount 会自动加 1。

快速失败的典型场景示例

场景1:迭代时其他线程修改集合结构

package com.collection;

import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * @author Jing61
 */
public class CollectionFailFast {
    public static void main(String[] args) {
        // 创建存储Integer的HashSet
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < 5; i++) {
            set.add(i);
        }

        // 第一个线程:每秒添加元素
        Thread addThread = new Thread(() -> {
            int count = 5;
            while (true) {
                try {
                    Thread.sleep(1000);
                    set.add(count++);
                    System.out.println("\n添加元素: " + (count - 1));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 第二个线程:迭代器遍历
        Thread iterateThread = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                    Iterator<Integer> iterator = set.iterator();
                    System.out.println("\n遍历:");
                    while (iterator.hasNext()) {
                        System.out.print(iterator.next() + " ");
                    }
                } catch (ConcurrentModificationException e) {
                    System.out.println("\n发生快速失败: " + e.getClass().getSimpleName());
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        addThread.start();
        iterateThread.start();
    }
}

避免快速失败的解决方案

快速失败的本质是“迭代器与集合修改操作的并发冲突”,可通过以下方式解决:

迭代期间锁住集合,禁止并发修改

利用同步集合本身的锁对象(或显式锁),确保迭代和修改操作互斥:

package com.collection;

import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * @author Jing61
 */
public class CollectionFailFast {
    public static void main(String[] args) {
        // 创建存储Integer的HashSet
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < 5; i++) {
            set.add(i);
        }

        // 第一个线程:每秒添加元素
        Thread addThread = new Thread(() -> {
            int count = 5;
            while (true) {
                synchronized (set) {
                    try {
                        Thread.sleep(1000);
                        set.add(count++);
                        System.out.println("\n添加元素: " + (count - 1));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        });

        // 第二个线程:迭代器遍历
        Thread iterateThread = new Thread(() -> {
            while (true) {
                synchronized (set) {
                    try {
                        Thread.sleep(1000);
                        Iterator<Integer> iterator = set.iterator();
                        System.out.println("\n遍历:");
                        while (iterator.hasNext()) {
                            System.out.print(iterator.next() + " ");
                        }
                    } catch (ConcurrentModificationException e) {
                        System.out.println("\n发生快速失败: " + e.getClass().getSimpleName());
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        addThread.start();
        iterateThread.start();
    }
}
posted @ 2025-11-14 16:28  Jing61  阅读(2)  评论(0)    收藏  举报