日常开发中常见的“锁”汇总

常见锁的应用场景对比

锁类型 特点 适用场景 实现方式
乐观锁 高性能,但需处理冲突检测 读多写少,低冲突 使用版本号或 CAS
悲观锁 数据安全,但性能较低 写多读少,高冲突 使用数据库的 for updatesynchronized 实现
自旋锁 减少上下文切换开销 锁竞争少,持锁时间短 使用 Atomicxxx包下面的类 和循环
读写锁 提高读性能 读多写少,允许并发读取 使用 ReentrantReadWriteLock 实现
可重入锁 线程可以重入锁 嵌套锁场景 使用 ReentrantLocksynchronized
偏向锁 偏向单线程场景,性能极高 无竞争的单线程场景 JVM 自动优化(synchronized 初始状态)
轻量级锁 竞争少,通过自旋优化 短期锁竞争 JVM 自动优化(synchronized 竞争升级)
重量级锁 阻塞线程,性能较低 高竞争,锁时间较长 JVM 自动优化(synchronized 高竞争升级)
分段锁 细化锁粒度,提升并发性 数据分区操作 jdk1.7之前的ConcurrentHashMap 的分段机制
统一锁 支持乐观读、悲观写结合 复杂并发场景 使用 StampedLock 实现

1. 乐观锁与悲观锁

乐观锁

  • 特点:假设并发冲突较少,每次操作前不加锁,操作后通过版本号或 CAS(Compare And Swap)检测冲突。
  • 实现方式CAS
  • 优点:无需线程阻塞,性能较高。
  • 适用场景:读多写少的场景。

悲观锁

  • 特点:假设并发冲突较多,每次操作前加锁,保证线程互斥。
  • 实现方式synchronizedReentrantLock
  • 优点:数据安全。
  • 适用场景:写多读少的场景。

2. 自旋锁

  • 特点:线程通过自旋(不断尝试)等待锁,而非直接进入阻塞状态。
  • 实现方式:依赖 CAS 操作。
  • 优点:避免线程上下文切换的开销。
  • 缺点:锁竞争激烈时会浪费 CPU 资源。

3. CAS(Compare And Swap)

  • 特点:一种无锁并发机制,通过比较内存中的值是否为期望值来更新数据。
  • 实现类AtomicInteger, AtomicLong, ConcurrentHashMap 等。
  • 优点:高效。
  • 缺点:可能引发 ABA 问题,需要配合版本号解决。

4. 排他锁

  • 特点:一次只允许一个线程获取锁,其他线程阻塞。
  • 实现方式synchronized, ReentrantLock
  • 应用场景:需要严格同步的场景。

5. 读写锁

  • 特点:分为读锁和写锁,允许多个线程同时读,但写操作互斥。
  • 实现类ReentrantReadWriteLock
  • 优点:提高读多写少场景的性能。

6. 可重入锁

  • 特点:允许同一线程多次获取同一把锁,而不会导致死锁。
  • 实现类ReentrantLock, synchronized
  • 优势:避免嵌套锁时死锁的问题。

7. 分段锁

  • 特点:将锁分段管理,每段锁控制不同数据的访问。
  • 实现类ConcurrentHashMap(JDK 7 使用)。
  • 优点:减少锁粒度,提高并发性能。

8. 偏向锁、轻量级锁、重量级锁(synchronized 内部优化)

偏向锁

  • 特点:当锁只被一个线程持有时,记录线程 ID,避免加锁操作。
  • 优点:性能极高。
  • 限制:当锁被其他线程竞争时,升级为轻量级锁。

轻量级锁

  • 特点:通过 CAS 自旋尝试获取锁。
  • 优点:减少线程阻塞开销。
  • 限制:当自旋超过阈值时,升级为重量级锁。

重量级锁

  • 特点:线程阻塞等待锁释放,使用 OS 同步机制。
  • 缺点:性能较低,线程上下文切换开销大。

9. 统一锁(StampedLock)

  • 特点StampedLock 提供乐观读锁和悲观写锁的组合。
  • 实现类StampedLock
  • 优点:读多写少场景性能优于 ReentrantReadWriteLock

10. 线程局部锁

  • 特点:将锁粒度细化到每个线程独享的资源上,减少竞争。
  • 实现方式ThreadLocal 或每个线程独占锁。

简单实现

// 1. 乐观锁实现(版本号机制)
class OptimisticLockExample {
    private int version = 0;

    public synchronized boolean updateData(int expectedVersion) {
        if (this.version == expectedVersion) {
            this.version++;
            return true; // 更新成功
        }
        return false; // 更新失败
    }
}

// 2. 自旋锁实现
class SpinLock {
    private AtomicBoolean lock = new AtomicBoolean(false);

    public void lock() {
        while (!lock.compareAndSet(false, true)) {
            // 自旋
        }
    }

    public void unlock() {
        lock.set(false);
    }
}

// 3. CAS 实现
class CASExample {
    private AtomicInteger value = new AtomicInteger(0);

    public int increment() {
        int current;
        do {
            current = value.get();
        } while (!value.compareAndSet(current, current + 1));
        return current + 1;
    }
}

// 4. 排他锁(synchronized 示例)
class ExclusiveLock {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

// 5. 读写锁实现
class ReadWriteLockExample {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private int value = 0;

    public void write(int newValue) {
        rwLock.writeLock().lock();
        try {
            value = newValue;
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public int read() {
        rwLock.readLock().lock();
        try {
            return value;
        } finally {
            rwLock.readLock().unlock();
        }
    }
}

// 6. 可重入锁实现
class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        lock.lock();
        try {
            // Critical section
        } finally {
            lock.unlock();
        }
    }
}

// 7. 分段锁实现(ConcurrentHashMap 示例)
class SegmentLockExample {
    private final ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();

    public void put(int key, String value) {
        map.put(key, value);
    }

    public String get(int key) {
        return map.get(key);
    }
}

// 8. 偏向锁、轻量级锁、重量级锁(synchronized 自动优化)
class SynchronizedExample {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }
}

// 9. StampedLock 示例
class StampedLockExample {
    private final StampedLock lock = new StampedLock();
    private int value = 0;

    public void write(int newValue) {
        long stamp = lock.writeLock();
        try {
            value = newValue;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    public int optimisticRead() {
        long stamp = lock.tryOptimisticRead();
        int currentValue = value;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try {
                currentValue = value;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return currentValue;
    }
}

// 10. ThreadLocal 实现线程局部锁
class ThreadLocalLockExample {
    private ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);

    public void set(int value) {
        threadLocal.set(value);
    }

    public int get() {
        return threadLocal.get();
    }
}

日常开发使用Redisson的最佳实现

1.加锁时使用tryLock()方法
trylock()方法可以设置等待时长,释放锁的时间可以避免因为直接使用lock()线程一直等待导致死锁问题。

2.在catch到线程中断时,使用Thread.currentThread().interrupt()恢复中断状态
Thread.currentThread().interrupt()是一种推荐的安全实践,确保线程中断信号能够正确传播,不会因为 InterruptedException 的捕获而被意外丢失。

  • 如果继续执行逻辑(如抛出自定义异常),需要调用 Thread.currentThread().interrupt() 来恢复中断状态。
  • 如果直接返回或终止线程,则不需要特别处理中断标志。

3.释放锁时判断是否持有锁isHeldByCurrentThread()
这是 ReentrantLock 提供的一个方法,用于判断当前线程是否持有该锁。这可以防止在以下场景中错误地调用 unlock():

  • 如果当前线程未持有锁而调用 unlock(),会抛出 IllegalMonitorStateException 异常。
  • 通过此检查,保证只有当前持有锁的线程才会尝试释放锁。

import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
 * 订单表 服务实现类
 *
 * @author wds
 * @since 2024-11-27
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

   /**
     * 下单
     *
     * @param reqDto 下单参数
     * @return Void
     */
    @Override
    @GlobalTransactional
    @Transactional(rollbackFor = Exception.class)
    public R<Void> add(OrderAddReq reqDto) {

        String hash = MD5Utils.encrypt2HexString(JacksonUtils.toJson(reqDto));
        RLock lock = redisson.getLock(String.format(OrderRedisKeyConstants.ORDER_ADD_HASH, hash));
        try {
            boolean isLock = lock.tryLock(1000, 15000, TimeUnit.MILLISECONDS);
            if (!isLock) {
                throw new BusinessException(OrderBusinessCodeEnum.ORDER_ADD_TO_FREQUENT);
            }

            OrderDetailCreateDto orderDetailCreateDto = valOrder(reqDto);
            Order order = reqDto.buildOrder(orderDetailCreateDto);
            log.info("订单:{}", JacksonUtils.toJson(order));
            AccountChangeReq accountChangeReq = RemoteAccountConvert.convert2OrderPay(order);
            R<Void> remoteTransferResult = accountFeign.payOrder(accountChangeReq, SecurityConstants.FROM_IN);
            if (!remoteTransferResult.isSuccess()) {
                log.error("调用账户服务支付失败,CoinExchangeReqDTO:{},remoteTransferResult:{}", JacksonUtils.toJson(accountChangeReq), remoteTransferResult);
                throw new BusinessException(ExtraCodeEnum.DATA_DEAL_ERROR.code, remoteTransferResult.getMsg());
            }
            if (!save(order.pay())) {
                log.error("订单插入数据库失败,order:{}", JacksonUtils.toJson(order));
                throw new BusinessException(ExtraCodeEnum.SQL_ERROR.code, "order.order_add_failed");
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("下单异常,reqDto:{}", JacksonUtils.toJson(reqDto), e);
           throw new BusinessException(ExtraCodeEnum.DATA_DEAL_ERROR.code, "guess.order.order_add_failed");
        }finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return R.ok();
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容