使用 collections.synchronizedlist 可以将一个普通的 arraylist 包装成线程安全的列表,其核心做法是调用该方法并传入目标列表,返回的对象会在每次单步操作时自动获取内部锁。需要注意的是,该包装类仅保证单个方法的原子性,迭代等复合操作必须手动在同步块中加锁,否则可能抛出 concurrentmodificationexception。

如何创建并使用同步列表进行基础读写
通过 Collections.synchronizedList(new ArrayList()) 即可创建实例,随后像普通列表一样调用 add、get 等方法,这些操作会自动获得对象监视器锁以保障线程安全。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SyncListDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
List<String> syncList = Collections.synchronizedList(list);
// 线程A:添加元素
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
syncList.add("Item-" + i);
}
});
// 线程B:读取元素
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(syncList.get(i));
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}运行上述代码后,控制台将按顺序打印 Item-0 到 Item-4。由于 add 和 get 都是单步方法,它们各自被同步块包裹,因此即使多线程并发访问也不会出现数据竞争或索引越界异常(前提是索引存在)。
立即学习“Java免费学习笔记(深入)”;
为什么迭代时必须手动加锁
虽然 synchronizedList 的 iterator() 方法本身是同步的,但它返回的迭代器并不具备“快速失败”之外的额外保护,如果在迭代过程中其他线程修改了列表结构,就会触发 ConcurrentModificationException。因此,必须在外部显式地锁定列表对象。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class SafeIterationDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
List<String> syncList = Collections.synchronizedList(list);
syncList.add("A");
syncList.add("B");
syncList.add("C");
// 正确的迭代方式:手动加锁
synchronized (syncList) {
Iterator<String> it = syncList.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
// 注意:不能在迭代期间直接调用 syncList.remove()
}
}
}
}执行这段代码会依次输出 A、B、C。关键在于 synchronized (syncList) 这一行,它确保了从获取迭代器到遍历结束的整个时间段内,没有其他线程能插入或删除元素。如果不加这个锁,当另一个线程正在执行 add 操作时,当前迭代器的状态可能会失效,导致程序崩溃。
在迭代过程中移除元素的具体问题
如果在同步块的迭代过程中尝试直接调用 syncList.remove(),会因为迭代器与底层集合的状态不一致而报错。正确的做法是使用迭代器自身的 remove() 方法,或者在同步块内重新获取迭代器。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class RemoveDuringIterationDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
List<String> syncList = Collections.synchronizedList(list);
syncList.add("X");
syncList.add("Y");
syncList.add("Z");
synchronized (syncList) {
Iterator<String> it = syncList.iterator();
while (it.hasNext()) {
String item = it.next();
if (item.equals("Y")) {
it.remove(); // 安全移除
}
}
}
System.out.println(syncList); // 输出 [X, Z]
}
}运行结果将输出 [X, Z]。这里 it.remove() 是迭代器提供的安全删除机制,它会同时更新迭代器和底层集合的状态,避免了并发修改检查的触发。如果改用 syncList.remove(item),则需要在每次删除后重新获取迭代器,因为底层的 modCount 会改变,导致当前迭代器失效。


















