当前位置: 首页 > article >正文

锁原理和使用

在 Java 和数据库开发中,不同类型的锁被广泛应用来确保数据一致性和线程安全。以下是常用锁的分类及其实现原理详解,并提供了相应的示例。


1. 公平锁和非公平锁

  • 公平锁:线程按请求顺序获取锁,避免“插队”。
  • 非公平锁:允许线程在队列中“插队”,可能导致部分线程长时间等待。

实现方式ReentrantLock 支持公平和非公平锁,默认是非公平锁。

// 公平锁
ReentrantLock fairLock = new ReentrantLock(true);
// 非公平锁
ReentrantLock nonFairLock = new ReentrantLock(false);

2. 可重入锁

可重入锁允许同一线程多次获取同一锁,防止因自己再次获取锁而导致死锁。ReentrantLocksynchronized 都是可重入锁。

public class ReentrantExample {
    public synchronized void methodA() {
        methodB(); // 当前线程可以再次进入
    }

    public synchronized void methodB() {
        // 逻辑代码
    }
}

3. 递归锁

递归锁允许线程在持有锁的情况下递归调用获取该锁。它是可重入锁的实现方式之一。ReentrantLock 是递归锁的典型实现。


4. 自旋锁

自旋锁在等待获取锁时,不立即阻塞线程,而是通过循环尝试。适合锁的持有时间较短的场景,减少了上下文切换的开销。

public class SpinLock {
    private AtomicReference<Thread> owner = new AtomicReference<>();

    public void lock() {
        Thread current = Thread.currentThread();
        while (!owner.compareAndSet(null, current)) {
            // 自旋等待
        }
    }

    public void unlock() {
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }
}

5. 读写锁

读写锁分为读锁和写锁,多个线程可以同时读取数据,但写操作是独占的。典型实现是 ReentrantReadWriteLock

ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
try {
    // 读取操作
} finally {
    lock.readLock().unlock();
}

lock.writeLock().lock();
try {
    // 写入操作
} finally {
    lock.writeLock().unlock();
}

6. 悲观锁和乐观锁

  • 悲观锁:假定并发冲突频繁,数据操作前即上锁,如数据库的 SELECT ... FOR UPDATE
  • 乐观锁:假定并发冲突较少,通过版本号等机制在提交时验证数据是否被修改。
// 乐观锁示例
public void updateWithOptimisticLock(int version) {
    // 更新时检查 version 是否相同
    String sql = "UPDATE table SET column = ? WHERE version = ?";
}

7. 行锁和表锁

  • 行锁:锁定特定行,如 InnoDB 支持行锁。
  • 表锁:锁定整个表,MyISAM 仅支持表锁。

行锁更适合高并发写操作,表锁适合大量读操作的场景。


8. 排它锁和共享锁

  • 排它锁(独占锁):同一时间只允许一个事务访问数据。
  • 共享锁:允许多个事务同时读取数据,但不允许写入。

SELECT ... LOCK IN SHARE MODE 创建共享锁。


9. 死锁

死锁是指两个或多个线程相互持有对方需要的锁,导致永久等待。避免死锁的方法包括:

  • 保持锁定顺序一致
  • 使用定时锁,尝试锁超时失败
  • 减少锁的持有时间

10. 分布式锁

分布式锁用于确保分布式系统中资源的独占性。Redis 和 Zookeeper 是常用的实现工具。典型的 Redis 分布式锁示例:

// Redis分布式锁示例
String lockKey = "resource_lock";
String requestId = UUID.randomUUID().toString();

// 加锁
boolean lock = redis.set(lockKey, requestId, "NX", "PX", 30000); // NX:不存在时设置, PX:设置超时

// 解锁
if (requestId.equals(redis.get(lockKey))) {
    redis.del(lockKey);
}

11. AQS(AbstractQueuedSynchronizer)

AQS 是 Java 提供的一个用于构建锁和同步器的底层框架,基于一个先进先出的队列来管理锁的获取和释放。ReentrantLockSemaphoreCountDownLatch 等类均基于 AQS 实现。


12. Synchronized 原理

synchronized 是 Java 的内置锁,通过对象监视器实现。其底层是基于 Monitor 锁机制,每个对象包含一个 Monitor,实现方法包括:

  • 对象头的 Mark Word:记录线程的锁定状态。
  • 锁升级机制:从偏向锁、轻量级锁到重量级锁,以适应不同并发量的场景。

示例

public synchronized void syncMethod() {
    // 方法体
}

13. 锁的使用示例

// 使用ReentrantLock可重入锁示例
public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        lock.lock();
        try {
            System.out.println("任务执行中...");
        } finally {
            lock.unlock();
        }
    }
}

// 使用Synchronized实现同步块
public class SynchronizedExample {
    public void synchronizedTask() {
        synchronized (this) {
            System.out.println("同步任务执行中...");
        }
    }
}

总结

在高并发编程和分布式系统中,不同锁的特性决定了其适用场景。了解各种锁的原理和使用方法,能够帮助开发者在不同场景中选择合适的锁机制,从而实现线程安全并优化性能。


http://www.kler.cn/a/376769.html

相关文章:

  • 实体(Entity)详解
  • SpringBoot整合EasyExcel加Vue
  • Docker BUG排查
  • pycharm调用方法时显示为灰色
  • 【深入浅出】深入浅出Bert(附面试题)
  • CSS--两列网页布局,三列布局和多行多列布局
  • Docker 安装HomeAssistant智能家居系统
  • uni-app实现app展示进度条在线更新以及定时更新提醒
  • 懂前端的都知道这里的门道有多深 - js 的事件循环
  • git am使用详解
  • 共模噪声和差模噪声
  • CentOS 7系统下Redis Cluster集群一键部署脚本发布
  • C++学习笔记----9、发现继承的技巧(六)---- 有趣且令人迷惑的继承问题(7)
  • 爬虫ip与反爬虫的“猫鼠游戏”
  • 萌熊数据科技:剑指脑机转入,开启科技新篇章
  • 机器学习实战:从数据准备到模型部署
  • 网关如何传递信息给微服务
  • 虚拟机安装Ubuntu系统
  • Kafka物理存储机制深度解析
  • 市场分化!汽车零部件「变天」
  • 《化学试剂》
  • linux8在线扩容/home目录
  • Redis中String 的底层实现是什么?
  • 读书笔记--类加载器
  • 深入理解网络协议:OSPF、VLAN、NAT与ACL详解
  • 学习正则表达式,如何校验手机号与电子邮箱