面试题答案
一键面试- 将普通集合转换为线程安全集合的方法
- 可以使用
Collections.synchronizedList
方法将ArrayList
转换为线程安全集合。示例代码如下:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ThreadSafeListExample { public static void main(String[] args) { List<String> nonThreadSafeList = new ArrayList<>(); List<String> threadSafeList = Collections.synchronizedList(nonThreadSafeList); } }
- 可以使用
- 线程安全机制
Collections.synchronizedList
返回的是一个SynchronizedRandomAccessList
(如果原列表实现了RandomAccess
接口)或SynchronizedList
。- 这些类在其方法(如
add
、get
、remove
等)中使用了synchronized
关键字来同步访问。例如,SynchronizedList
的add
方法可能类似这样实现:
public boolean add(E e) { synchronized (mutex) { return list.add(e); } }
- 这里的
mutex
是用于同步的对象,通常是返回的同步列表本身。这意味着在同一时间,只有一个线程可以访问列表的方法,从而保证了线程安全。
- 性能问题
- 锁竞争开销:由于所有对列表的操作都需要获取锁,在高并发环境下,多个线程频繁竞争锁会导致性能瓶颈。例如,一个线程在执行
add
操作时持有锁,其他线程想要执行get
操作也需要等待锁的释放,即使get
操作本身并不需要修改列表状态,这就造成了不必要的等待。 - 可伸缩性问题:随着线程数量的增加,锁竞争会变得更加激烈,系统的可伸缩性会受到影响。
- 锁竞争开销:由于所有对列表的操作都需要获取锁,在高并发环境下,多个线程频繁竞争锁会导致性能瓶颈。例如,一个线程在执行
- 解决方案
- 使用
CopyOnWriteArrayList
:CopyOnWriteArrayList
适用于读多写少的场景。它的原理是在进行写操作(如add
、remove
)时,会复制一份原数组,在新数组上进行修改,然后将原引用指向新数组。读操作(如get
)则直接读取原数组,不需要加锁。- 示例代码:
import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class CopyOnWriteArrayListExample { public static void main(String[] args) { List<String> list = new CopyOnWriteArrayList<>(); list.add("element"); String element = list.get(0); } }
- 使用
ConcurrentLinkedQueue
:- 对于队列操作,
ConcurrentLinkedQueue
是一个线程安全的无界队列。它采用无锁数据结构,使用CAS
(Compare - And - Swap)操作来实现线程安全。适用于需要高效的并发队列操作场景,如生产者 - 消费者模型。 - 示例代码:
import java.util.concurrent.ConcurrentLinkedQueue; public class ConcurrentLinkedQueueExample { public static void main(String[] args) { ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>(); queue.add("element"); String element = queue.poll(); } }
- 对于队列操作,
- 读写锁(
ReadWriteLock
):- 如果需要更细粒度的控制,可以使用
ReadWriteLock
。读操作可以并发执行,而写操作需要独占锁。例如,对于一个自定义的基于ArrayList
的集合,可以使用ReadWriteLock
来控制访问。 - 示例代码:
import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockList { private final List<String> list = new ArrayList<>(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); public void add(String element) { lock.writeLock().lock(); try { list.add(element); } finally { lock.writeLock().unlock(); } } public String get(int index) { lock.readLock().lock(); try { return list.get(index); } finally { lock.readLock().unlock(); } } }
- 如果需要更细粒度的控制,可以使用
- 使用