【004】Lock接口及其与synchronized的区别


Java中的 Lock接口及其与 synchronized的区别:

1、显式锁定: Lock是一个接口,提供了比synchronized更灵活的锁定机制。

2、可中断锁定: Lock允许尝试非阻塞地获取锁,或者在锁定期间响应中断。

3、公平性选择: Lock提供了选择公平锁或非公平锁的能力。

4、性能差异: 在不同情况下,Locksynchronized的性能表现有所不同。

一、核心比喻: 俩 “锁王” 的人设差异

先给它俩定个 “人设”,瞬间理解定位:

1. synchronized:小区门口的 “退休大爷保安”

  • 大爷自带 “门禁系统”,你进门他自动拦着别人,你出门他自动放行,不用你操心。但大爷脾气倔,认死理,功能单一,你跟他讲道理(比如 “我等半天了,能先让我进吗?”),他一概不听,只认 “先到先得,要么等要么走”。

2. Lock:你家请的 “智能门锁 + 私人管家”(比如 ReentrantLock)

  • 门锁是你自己装的,得手动刷卡(lock())进门,出门必须记得手动锁门(unlock()),忘了锁门就等于 “敞着门睡觉”。但管家超灵活,能帮你干好多大爷干不了的事:比如 “等 5 分钟没人来就别等了”(超时等待)、“有人插队就打断他”(可中断)、“按排队顺序来,不允许加塞”(公平锁)。

二、使用方式:一个 “全自动”,一个 “手动挡”

⬆️ 1. synchronized: 大爷帮你 “包办一切”

  • 用 synchronized 就像走小区门禁,你只要往门口一站(进入同步方法 / 代码块),大爷自动锁门;你走了(方法 / 代码块结束),大爷自动开门,全程不用你动手。
// 同步方法:相当于“进小区大门,大爷直接拦着别人”
public synchronized void goHome() {
    System.out.println("我进家门了,别人等着吧!");
}

// 同步代码块:相当于“进家门的客厅,大爷只拦客厅门口”
public void goLivingRoom() {
    synchronized(this) { // this 就是“客厅门”
        System.out.println("我进客厅了,别人别进来抢沙发!");
    }
}

特点:语法简洁,“自动加锁 + 自动释放”,像 “全自动洗衣机”,不用管细节,但也没机会 “手动调程序”。

➡️ 2. Lock: 管家让你 “自己动手,丰衣足食”

  • 用 Lock 就像用智能门锁,你得自己刷卡进门,出门必须手动锁门,还得把 “锁门” 这事记死 —— 万一忘了,管家可不会帮你补锁。
// 手动加锁+手动释放:少一步都不行!
Lock lock = new ReentrantLock(); // 装个智能门锁

public void goHome() {
    lock.lock(); // 刷卡进门(手动加锁)
    try {
        System.out.println("我进家门了,别人等着吧!");
    } finally {
        lock.unlock(); // 出门必须锁门(手动释放),放finally里才保险!
    }
}

特点:语法繁琐,但 “手动挡” 意味着 “可控性强”。就像开手动挡车,虽然麻烦,但能玩出 “漂移”(各种高级功能),而 synchronized 就是自动挡,只能乖乖开。

三、核心差异:大爷 vs 管家,能力天差地别

用一张 “人设对比表”,把差异怼到你眼前:

对比维度synchronized(退休大爷)Lock(智能管家)形象比喻
实现层面JVM 内置实现(字节码 + 监视器锁),底层是 “黑盒”Java 代码实现(基于条件队列),底层是 “白盒”大爷是 “体制内退休”,背后有人撑腰;管家是 “外包精英”,啥都敢干
锁释放方式自动释放(方法 / 代码块结束时)必须手动释放(finally 中写 unlock()大爷 “目送你进门”,自动关门;管家 “你走了不锁门,他就当没看见”
可中断性获取锁时不可中断(死等,除非线程被终止)lockInterruptibly() 可中断(等半天没人来,能 “溜”)大爷:“你要么等我放行,要么滚,别跟我 bb”;管家:“等 5 分钟还没人,你先去买杯奶茶?”
尝试获取锁不支持(要么拿到,要么一直等)tryLock() 可立即返回(拿不到就走),还能设超时大爷:“没轮到你就站那别动!”;管家:“试一下门开不开,开不了我先去干别的”
公平锁不支持(谁挤得快谁进,不管排队)可选公平锁(new ReentrantLock(true),按顺序来)大爷:“内卷之王!谁猛谁先上”;管家:“排队买奶茶,先到先得,不允许加塞”
多个条件变量不支持(只有一个 “等待队列”)支持多个 Condition(能分 “客厅等”“卧室等”)大爷:“所有人都在大门外等!”;管家:“要等客厅的去客厅门口,等卧室的去卧室门口”
性能JDK1.6 后优化很多(偏向锁、轻量级锁),低竞争下差不多高并发场景更灵活,复杂场景性能更优大爷 “退休后学了新技能”(优化后),简单活干得快;管家 “专业对口”,复杂活干得好
可重入性可重入(比如递归调用同步方法,不会死锁)可重入(ReentrantLock 名字就带 “重入”)大爷认熟人:“你上次进过,这次还能进”;管家认钥匙:“你有钥匙,再开多少次都行”

四、经典场景: 该找大爷还是管家?

✅ 找大爷(synchronized)的场景:

  • 简单同步:比如 “给对象的某个字段加 1”“判断一个变量是否为空”。
  • 怕麻烦:不想写 try-finally,怕忘了释放锁。
  • 低并发:比如小区人少,没人抢门禁,大爷慢悠悠也能搞定。

例子:家庭群抢红包,就用大爷看着,谁先点到红包谁拿,简单粗暴。

⬆️ 找管家(Lock)的场景:

  • 需要 “超时等待”:比如 “尝试获取锁 5 秒,拿不到就放弃,别死等”。
  • 需要 “公平排队”:比如银行办理业务,必须按取号顺序来,不能让 “插队党” 得逞。
  • 需要 “多条件等待”:比如线程 A 等 “客厅没人”,线程 B 等 “卧室没人”,得分开等。
  • 高并发复杂场景:比如电商秒杀,既要控制并发量,又要处理 “有人超时放弃” 的情况。

例子:双十一抢购,管家先让大家排队(公平锁),等 10 秒还没抢到的就提示 “手慢了”(超时),有人中途退出(中断)就把名额让给下一个,效率超高。

五、常见坑: 别被 “锁” 坑死!

🚀 1. Lock 的最大坑:忘了释放锁!
🚀 1. Lock 的最大坑:忘了释放锁!
🚀 1. Lock 的最大坑:忘了释放锁!
🚀 1. Lock 的最大坑:忘了释放锁!
🚀 1. Lock 的最大坑:忘了释放锁!
🚀 1. Lock 的最大坑:忘了释放锁!

如果不用 finally 包 unlock(),一旦临界区代码抛异常,锁就永远不会释放,其他线程全堵死 —— 相当于 “你出门没锁门,结果家里被搬空了”。

// 错误示范:没放finally,抛异常就gg
public void goHome() {
    lock.lock();
    if (1 / 0 == 0) { // 抛异常了!
        System.out.println("永远到不了这里");
    }
    lock.unlock(); // 这行代码永远执行不到,锁没释放!
}

// 正确示范:finally 兜底,必释放锁
public void goHome() {
    lock.lock();
    try {
        if (1 / 0 == 0) { ... }
    } finally {
        lock.unlock(); // 就算抛异常,也会执行!
    }
}

➡️ 2. synchronized 的坑:死等没商量!

如果一个线程持有 synchronized 锁,另一个线程想获取,只能死等 —— 就算等 1 小时,也不能 “中途放弃”。比如:

// 线程A持有锁,一直不释放(比如死循环)
public synchronized void holdLock() {
    while (true) { // 死循环,锁永远不释放
        System.out.println("我就不放手!");
    }
}

// 线程B想获取锁,只能一直等,直到线程A被终止
public synchronized void waitLock() {
    System.out.println("我等到花儿都谢了!"); // 永远到不了这里
}

而 Lock 可以用 tryLock(5, TimeUnit.SECONDS),等 5 秒没拿到就走,不会死等。

六、总结:一句话选对 “锁王”

  • 简单活找大爷:synchronized 语法简洁,不易出错,适合简单同步场景(90% 的日常开发够用了)。
  • 复杂活找管家:Lock 功能强大,灵活可控,适合高并发、需要超时 / 公平锁 / 多条件的场景(比如框架、中间件开发)。

结论: synchronized 是 “躺平式锁王”,能自动搞定就绝不麻烦你;Lock 是 “内卷式锁王”,你给它指令,它能帮你卷死所有竞争对手!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值