面试题答案
一键面试-
减小锁粒度
- 原理:将大的锁保护区域拆分成多个小的锁保护区域,这样不同线程可以并行访问不同区域,减少锁竞争。
- 示例:假设有一个简单的库存管理系统,原本对整个库存操作使用一个大锁。
public class Inventory { private Map<String, Integer> stock = new HashMap<>(); private final Object lock = new Object(); // 原来的方法,使用一个大锁 public void updateStock(String item, int quantity) { synchronized (lock) { Integer current = stock.get(item); if (current == null) { current = 0; } stock.put(item, current + quantity); } } }
- 优化后:为每个商品创建独立的锁。
public class OptimizedInventory { private Map<String, Integer> stock = new HashMap<>(); private Map<String, Object> itemLocks = new HashMap<>(); public OptimizedInventory() { // 初始化每个商品的锁 List<String> items = Arrays.asList("item1", "item2", "item3"); for (String item : items) { itemLocks.put(item, new Object()); } } public void updateStock(String item, int quantity) { Object itemLock = itemLocks.get(item); synchronized (itemLock) { Integer current = stock.get(item); if (current == null) { current = 0; } stock.put(item, current + quantity); } } }
-
锁分离
- 原理:根据操作类型不同,使用不同的锁,比如读写锁分离,读操作共享锁,写操作独占锁。
- 示例:使用
ReentrantReadWriteLock
实现读写锁分离。
import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteExample { private Map<String, Object> data = new HashMap<>(); private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public Object read(String key) { lock.readLock().lock(); try { return data.get(key); } finally { lock.readLock().unlock(); } } public void write(String key, Object value) { lock.writeLock().lock(); try { data.put(key, value); } finally { lock.writeLock().unlock(); } } }
-
锁粗化
- 原理:如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,此时可以将加锁的范围扩展(粗化)到整个操作序列的外部,避免频繁加解锁带来的性能开销。
- 示例:
public class StringAppender { private final Object lock = new Object(); // 未优化,频繁加解锁 public String appendStrings(List<String> strings) { StringBuilder result = new StringBuilder(); for (String str : strings) { synchronized (lock) { result.append(str); } } return result.toString(); } // 优化后,锁粗化 public String optimizedAppendStrings(List<String> strings) { StringBuilder result = new StringBuilder(); synchronized (lock) { for (String str : strings) { result.append(str); } } return result.toString(); } }
-
使用偏向锁和轻量级锁
- 原理:
- 偏向锁:当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。
- 轻量级锁:在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。
- 示例:在JVM参数中开启偏向锁,
-XX:+UseBiasedLocking
。代码中使用synchronized
块,在偏向锁模式下,首次进入synchronized
块,锁会偏向当前线程。
public class BiasedLockExample { private final Object lock = new Object(); public void doSomething() { synchronized (lock) { // 同步代码块 } } }
- 注意:偏向锁在JDK 6及以后默认开启。轻量级锁是JVM在运行时根据情况自动优化使用的,一般不需要开发者手动干预。
- 原理:
-
使用并发工具类替代显式锁
- 原理:Java的并发包提供了一些线程安全且性能优化的工具类,如
ConcurrentHashMap
,它内部采用了分段锁等优化机制,相比自己使用synchronized
或ReentrantLock
实现线程安全的哈希表性能更好。 - 示例:
import java.util.concurrent.ConcurrentHashMap; public class ConcurrentMapExample { private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); public void put(String key, Integer value) { map.put(key, value); } public Integer get(String key) { return map.get(key); } }
- 这里
ConcurrentHashMap
在多线程环境下,多个线程可以同时对不同的段进行操作,减少锁竞争,提升性能。
- 原理:Java的并发包提供了一些线程安全且性能优化的工具类,如