【JAVA基础面经】线程安全的List

张开发
2026/6/2 6:41:41 15 分钟阅读
【JAVA基础面经】线程安全的List
线程安全的List前言CopyOnWriteArrayListCollections.synchronizedListReentrantReadWriteLock了解前言Java 提供了多种实现线程安全 List 的方式其中最常用的三种是方案核心机制适用场景CopyOnWriteArrayList写时复制读写分离读多写少如配置信息、监听器列表Collections.synchronizedList装饰器模式全方法加 synchronized读写均衡需要快速兼容旧代码手动 ReentrantReadWriteLock读写锁读共享写互斥高并发随机写需要精细化控制锁粒度CopyOnWriteArrayList核心原理写时复制Copy-On-Write读操作直接读取底层数组完全无锁性能极高。写操作add、set、remove先获取 ReentrantLock然后复制一份新数组在新数组上修改最后通过 volatile 变量将内部引用指向新数组。publicbooleanadd(Ee){finalReentrantLocklockthis.lock;lock.lock();try{Object[]elementsgetArray();intlenelements.length;Object[]newElementsArrays.copyOf(elements,len1);newElements[len]e;setArray(newElements);// volatile 写returntrue;}finally{lock.unlock();}}对于初始数组: [A, B, C]array指向旧数组当写操作执行 add(“D”)时会执行以下步骤加锁复制新数组: [A, B, C, D]将 array 指向新数组解锁CopyOnWriteArrayListStringlistnewCopyOnWriteArrayList();list.add(A);list.add(B);// 多线程读完全并行无锁newThread(()-{for(Strings:list){System.out.println(s);}}).start();// 写操作内部加锁复制数组list.add(C);Collections.synchronizedListCollections.synchronizedList 返回一个内部包装类 SynchronizedList它在所有方法调用外层包裹了 synchronized (mutex) 同步块。staticclassSynchronizedListEimplementsListE{finalListElist;finalObjectmutex;// 锁对象默认是 thispublicEget(intindex){synchronized(mutex){returnlist.get(index);}}publicbooleanadd(Ee){synchronized(mutex){returnlist.add(e);}}}包装器只保证单个方法调用的原子性但迭代遍历不是单个方法调用必须在外层手动加锁ListStringsyncListCollections.synchronizedList(newArrayList());// ❌ 错误示范这样遍历仍可能抛 ConcurrentModificationExceptionfor(Strings:syncList){System.out.println(s);}// ✅ 正确做法遍历时手动持有同一把锁synchronized(syncList){for(Strings:syncList){System.out.println(s);}}Collections.synchronizedList可以将任何 List 实现包装为线程安全的包括ArrayList、LinkedList而 Vector 只能自己实现。ReentrantReadWriteLock了解如果希望多个读操作可以并发多个线程同时读写操作互斥写写互斥读写互斥可以使用ReentrantReadWriteLock。importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.locks.ReadWriteLock;importjava.util.concurrent.locks.ReentrantReadWriteLock;publicclassReadWriteLockListE{privatefinalListElistnewArrayList();privatefinalReadWriteLocklocknewReentrantReadWriteLock();publicvoidadd(Ee){lock.writeLock().lock();try{list.add(e);}finally{lock.writeLock().unlock();}}publicEget(intindex){lock.readLock().lock();try{returnlist.get(index);}finally{lock.readLock().unlock();}}publicintsize(){lock.readLock().lock();try{returnlist.size();}finally{lock.readLock().unlock();}}// 遍历方法需要在外部加锁或者返回快照publicvoidforEach(ConsumerEaction){lock.readLock().lock();try{list.forEach(action);}finally{lock.readLock().unlock();}}}

更多文章