锁机制

85 阅读2分钟

当我们讨论锁机制时,通常会涉及以下几个方面的考虑:

  1. 锁的种类:

    • 悲观锁: 在访问数据时,假设会有其他线程来修改数据,因此在获取数据前先加锁,确保数据不会被其他线程修改。Java中的synchronized关键字和Lock接口的实现类都属于悲观锁。
    • 乐观锁: 假设在访问数据时不会有其他线程修改数据,只在更新数据时检查是否被其他线程修改。适合读操作多的场景。常用的实现方式是CAS(比较并交换),即在更新数据时检查数据是否被其他线程修改。
  2. 锁的升级与降级:

    • 锁的状态有四种:无锁、偏向锁、轻量级锁、重量级锁。锁状态只能升级不能降级。

      • 无锁: 多个线程直接竞争同一资源,只有一个线程能成功修改。
      • 偏向锁: 当同一线程多次访问同一资源时,该线程会自动获取锁,降低获取锁的代价。
      • 轻量级锁: 当多个线程竞争同一资源,会通过自旋等待的方式避免阻塞,提高性能。
      • 重量级锁: 当自旋等待不成功时,线程会阻塞,等待锁释放。适用于资源竞争激烈的场景。
  3. 锁的阻塞与非阻塞:

    • 阻塞锁: 线程在获取锁时如果失败,会被阻塞直到锁可用。常见的有公平锁和非公平锁。

      • 公平锁: 等待锁的线程按照先来后到的顺序获取锁。
      • 非公平锁: 等待锁的线程有机会插队,不按照先来后到的顺序获取锁,减小唤醒线程的开销。
    • 非阻塞锁: 线程在获取锁时如果失败,不会被阻塞,而是可以进行其他工作,定期检查锁是否可用。常见的是自旋锁。

  4. 锁的可重入性:

    • 可重入锁: 同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁,不会阻塞。Java的synchronizedReentrantLock都是可重入锁。
    • 非可重入锁: 不支持同一个线程在外层方法获取锁的时候,再进入内层方法自动获取锁。
  5. 锁的共享性:

    • 共享锁: 多个线程可以同时持有相同的锁,适用于读多写少的场景。
    • 排它锁: 一个线程独占锁,其他线程无法同时持有相同的锁,适用于写操作。