一文解析公平锁、非公平锁、悲观锁、乐观锁、可重入锁和锁的升级(含详细代码实例)

在并发开发和数据库事务处理中,锁机制是绕不开的难题。了解锁的种类、原理和应用场景,对于写出高效安全的并发程序非常重要。本文将系统总结公平锁、非公平锁、悲观锁、乐观锁、可重入锁、锁的升级等核心知识,并配套详细的实用代码,带你搞懂常见锁机制!


一、公平锁与非公平锁

1. 公平锁(Fair Lock)

定义:获取锁的顺序按照线程到来的顺序进行分配,先请求锁的线程先获得锁,避免线程“饥饿”。

应用场景:对执行先后顺序非常敏感的场合,如任务调度、并发队列等。

代码示例(Java)

import java.util.concurrent.locks.ReentrantLock;

public class FairLockDemo {
    // true 表示公平锁
    private static final ReentrantLock fairLock = new ReentrantLock(true);

    public void testLock() {
        fairLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 获得了公平锁");
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            fairLock.unlock();
        }
    }

    public static void main(String[] args) {
        FairLockDemo demo = new FairLockDemo();
        for (int i = 0; i < 5; i++) {
            new Thread(demo::testLock, "Thread-" + i).start();
        }
    }
}

2. 非公平锁(Nonfair Lock)

定义:线程加锁的顺序不严格按照申请顺序,允许“插队”,提高吞吐量。

应用场景:大多数并发场景,追求性能。

代码示例(Java)

import java.util.concurrent.locks.ReentrantLock;

public class NonFairLockDemo {
    // 默认 false,非公平锁
    private static final ReentrantLock lock = new ReentrantLock();

    public void testLock() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 获得了非公平锁");
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        NonFairLockDemo demo = new NonFairLockDemo();
        for (int i = 0; i < 5; i++) {
            new Thread(demo::testLock, "Thread-" + i).start();
        }
    }
}

拓展: synchronized 在 JVM 层面也是一种非公平锁。


二、悲观锁与乐观锁

1. 悲观锁(Pessimistic Lock)

定义:假设系统“很可能”发生并发冲突,每次数据操作都先加锁,其他线程只能等待。

应用场景:并发写多于读,且数据一致性要求高,例如订单、资金等核心业务。

数据库实现:

-- 事务中加锁(MySQL InnoDB)
START TRANSACTION;
SELECT * FROM account WHERE id=1 FOR UPDATE;
-- do something...
UPDATE account SET balance=balance-100 WHERE id=1;
COMMIT;

Java实现(synchronized / Lock):

public class PessimisticLockDemo {
    public synchronized void doSomething() {
        System.out.println(Thread.currentThread().getName() + " 获得了悲观锁");
        // 执行“临界区”操作
    }
}

2. 乐观锁(Optimistic Lock)

定义:假定冲突较少,不上锁,通过“版本号”或CAS检查数据是否被别人修改。若被修改则重试。

应用场景:用户数大、并发冲突不多的场景,如商品秒杀、用户信息查询等。

数据库实现(版本号字段)

-- 查询时一并获取 version,例如5
UPDATE account SET balance=balance-100, version=version+1 WHERE id=1 AND version=5;
-- 如果该SQL更新数为0,说明数据已被别的线程改过,需要重试

Java实现(CAS 原子类)

import java.util.concurrent.atomic.AtomicInteger;

public class OptimisticLockDemo {
    private AtomicInteger value = new AtomicInteger(0);

    public void tryUpdate() {
        int oldVal, newVal;
        do {
            oldVal = value.get();
            newVal = oldVal + 1;
        } while (!value.compareAndSet(oldVal, newVal));
        System.out.println("成功更新为:" + newVal);
    }
}

三、可重入锁(Reentrant Lock)

定义:同一线程多次申请同一把锁不会被阻塞,内部会维护锁“重入次数”。

作用:方便递归、父子方法重复加锁的场景,避免死锁。

1. synchronized的可重入性:

public class ReentrantSyncDemo {
    public synchronized void outer() {
        System.out.println("outer....");
        inner();
    }

    public synchronized void inner() {
        System.out.println("inner....");
    }

    public static void main(String[] args) {
        new ReentrantSyncDemo().outer();
    }
}

2. ReentrantLock的可重入性:

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();

    public void methodA() {
        lock.lock();
        try {
            System.out.println("进入methodA");
            methodB();
        } finally {
            lock.unlock();
        }
    }

    public void methodB() {
        lock.lock();
        try {
            System.out.println("进入methodB");
        } finally {
            lock.unlock();
        }
    }
}

四、锁的升级(锁的优化机制)

定义:JVM对synchronized的性能优化,锁的状态会随竞争程度自动“升级”,包括偏向锁、轻量级锁和重量级锁。

锁的状态与升级流程

  • 无锁:对象未被线程持有。
  • 偏向锁:假设只有一条线程反复获得锁,极高性能(无任何同步原语)。
  • 轻量级锁:短期竞争时的自旋锁(无需阻塞,性能高)。
  • 重量级锁:竞争激烈时的传统监视器锁(阻塞/唤醒)。

状态升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
(注意:不会降级)

代码观察(JDK源码、JOL工具辅助理解):

public class LockUpgradeDemo {
    public static void main(String[] args) throws Exception {
        Object obj = new Object();
        synchronized (obj) {
            // 查看对象头信息
            System.out.println("进入同步块");
        }
    }
}

使用JOL(Java Object Layout)库可以详细观察对象头信息及锁状态变化。


五、总结对比表

类型定义场景代码说明优缺点
公平锁先到先得,排队获取锁银行排队new ReentrantLock(true)避免饿死但慢
非公平锁不按顺序,允许插队绝大多数并发场景new ReentrantLock(false)synchronized性能高风险饿死
悲观锁先锁后用,强制同步核心交易、资金synchronized/数据库for update安全性能一般
乐观锁无锁冲突检测后重试高频读、低冲突Java原子类、版本号字段快,不适用多冲突
可重入锁自身可多次获得同一锁递归与多调用场景synchronizedReentrantLock灵活无死锁
锁的升级JVM智能优化锁粒度JVM自动控制synchronized(自动实现)性能最优动态选择

六、开发实践

  • 公平锁用于严格保证顺序,通常业务场景非必须;
  • 乐观锁适用于冲突概率小、性能要求高的场合;
  • 悲观锁安全但性能低,不适于高并发的热点资源;
  • 可重入锁极大提高编码灵活性,避免递归死锁;
  • JVM锁优化机制让大部分场合无需人工干涉锁粒度;
  • 项目开发建议优先选择简单的synchronized,高并发场景评估使用ReentrantLock及乐观锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值