# 42. 减少锁竞争的策略分析

# 标准答案

✅ 高并发环境下减少锁竞争的策略:

  1. 锁分段

    • 将一个大锁拆分为多个小锁
    • 降低锁粒度,减少竞争范围
    • 如ConcurrentHashMap的分段锁设计
  2. 锁消除

    • 使用ThreadLocal避免共享
    • 采用无锁数据结构(CAS)
    • 使用局部变量避免共享
  3. 锁粗化

    • 合并频繁加锁操作
    • 减少锁的请求次数
    • 避免反复加锁解锁
  4. 读写分离

    • 使用ReadWriteLock
    • 读多写少场景性能提升
    • 允许并发读操作

# 答案解析

# 1️⃣ 锁分段实现

public class SegmentLockMap<K, V> {
    private static final int SEGMENTS = 16;
    private final Object[] locks;
    private final Map<K, V>[] segments;
    
    public SegmentLockMap() {
        locks = new Object[SEGMENTS];
        segments = new Map[SEGMENTS];
        for (int i = 0; i < SEGMENTS; i++) {
            locks[i] = new Object();
            segments[i] = new HashMap<>();
        }
    }
    
    private int getSegment(K key) {
        return Math.abs(key.hashCode() % SEGMENTS);
    }
    
    public V put(K key, V value) {
        int segment = getSegment(key);
        synchronized (locks[segment]) {
            return segments[segment].put(key, value);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 2️⃣ 无锁设计

public class LockFreeCounter {
    private AtomicLong value = new AtomicLong(0);
    
    public long increment() {
        long current;
        long next;
        do {
            current = value.get();
            next = current + 1;
        } while (!value.compareAndSet(current, next));
        return next;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 常见误区

  • 误区1:过度使用锁分段导致内存占用增加
  • 误区2:盲目使用无锁操作导致性能下降

# 典型场景与解决方案

# ✅ 高并发计数器

public class HighConcurrencyCounter {
    // 分片计数器
    private final LongAdder counter = new LongAdder();
    // 本地计数器
    private final ThreadLocal<Long> localCounter = ThreadLocal.withInitial(() -> 0L);
    
    public void increment() {
        // 先在本地计数
        localCounter.set(localCounter.get() + 1);
        // 定期同步到全局计数器
        if (localCounter.get() % 100 == 0) {
            counter.add(localCounter.get());
            localCounter.set(0L);
        }
    }
    
    public long getCount() {
        // 合并所有线程的本地计数
        long total = counter.sum();
        for (Thread thread : Thread.getAllStackTraces().keySet()) {
            ThreadLocal<Long> local = localCounter;
            if (local != null) {
                total += local.get();
            }
        }
        return total;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 企业实战经验

# Situation(业务背景)

电商订单系统在秒杀场景下,库存锁竞争严重。

# Task(核心任务)

优化库存锁设计,提高并发性能。

# Action(解决方案)

  1. 实现分段库存锁
  2. 使用本地缓存减少锁访问
  3. 采用乐观锁机制
  4. 引入预扣减机制

# Result(结果)

  • 系统TPS提升300%
  • 锁等待时间减少80%
  • 秒杀成功率提升50%

# 深入追问

🔹 如何选择合适的锁分段数量?

  • 考虑并发访问量
  • 评估内存占用
  • 进行性能测试

🔹 在什么情况下不适合使用锁分段?

  • 数据强一致性要求
  • 频繁的跨段操作
  • 内存资源受限

# 相关面试题

  1. 如何实现高效的分段锁?
  2. CAS操作的优缺点是什么?
  3. 如何在保证线程安全的同时提高性能?