MST

星途 面试题库

面试题:Java多线程锁的优化策略

在高并发场景下,如何对Java中的锁(如synchronized、ReentrantLock等)进行优化以提升性能?请结合具体代码示例阐述。
37.3万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. 减小锁粒度

    • 原理:将大的锁保护区域拆分成多个小的锁保护区域,这样不同线程可以并行访问不同区域,减少锁竞争。
    • 示例:假设有一个简单的库存管理系统,原本对整个库存操作使用一个大锁。
    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);
            }
        }
    }
    
  2. 锁分离

    • 原理:根据操作类型不同,使用不同的锁,比如读写锁分离,读操作共享锁,写操作独占锁。
    • 示例:使用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();
            }
        }
    }
    
  3. 锁粗化

    • 原理:如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,此时可以将加锁的范围扩展(粗化)到整个操作序列的外部,避免频繁加解锁带来的性能开销。
    • 示例
    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();
        }
    }
    
  4. 使用偏向锁和轻量级锁

    • 原理
      • 偏向锁:当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程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在运行时根据情况自动优化使用的,一般不需要开发者手动干预。
  5. 使用并发工具类替代显式锁

    • 原理:Java的并发包提供了一些线程安全且性能优化的工具类,如ConcurrentHashMap,它内部采用了分段锁等优化机制,相比自己使用synchronizedReentrantLock实现线程安全的哈希表性能更好。
    • 示例
    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在多线程环境下,多个线程可以同时对不同的段进行操作,减少锁竞争,提升性能。