咱们用「生活例子」+「通俗语言」重新讲,不搞复杂概念,只抓核心逻辑~
先记住一个核心:锁的作用就是 “抢资源”—— 让多个线程(比如多个人)按规则用同一个共享资源(比如打印机、厕所),别乱套。
一、先搞懂 3 个基础概念(类比生活)
- 共享资源:多个人要抢着用的东西(比如公司打印机、公共厕所、代码里的共享变量)。
- 临界区:用共享资源的 “过程”(比如用打印机打印文件、用厕所的时间、代码里操作共享变量的几行代码)。
- 锁:控制谁能进临界区的 “规则”(比如打印机前排队、厕所门的锁、代码里的同步机制)。
二、Java 里的锁,按 “好懂程度” 排序
1. 最傻瓜的锁:synchronized(隐式锁)
相当于「厕所门的自动锁」—— 进去后门自动锁,出来后自动开,不用你手动操作,简单粗暴。
怎么用?(3 种场景,类比理解)
- 场景 1:修饰实例方法 → 锁 “当前对象”(比如你占了公司的某个打印机,别人不能用这台)。
public class Printer { // 锁的是 Printer 的实例(比如 printer1、printer2 是不同锁) public synchronized void print() { System.out.println("打印中..."); // 临界区(用打印机的过程) } } - 场景 2:修饰静态方法 → 锁 “整个类”(比如公司所有打印机都被你占了,别人一台都不能用)。
public class Printer { // 锁的是 Printer 这个类(所有实例共用一把锁) public static synchronized void printAll() { System.out.println("所有打印机都被我用了..."); } } - 场景 3:修饰代码块 → 锁 “自定义对象”(比如你只占打印机的 “复印功能”,别人还能用来打印)。
public class Printer { private final Object copyLock = new Object(); // 专门管“复印”的锁 public void copy() { synchronized (copyLock) { // 只锁复印功能 System.out.println("复印中..."); } } public void print() { // 打印功能不受影响,别人能同时用 System.out.println("打印中..."); } }
核心特点:
- 不用手动开锁 / 关锁(JVM 帮你弄),新手闭眼用。
- 别人用的时候,你只能排队等(非公平,可能有人插队,效率高)。
- 你自己可以反复进(比如你在厕所里,还能再开一次锁,不会卡住)→ 可重入。
2. 更灵活的锁:ReentrantLock(显式锁)
相当于「手动开的保险柜」—— 要自己用钥匙开锁(lock()),用完必须手动关锁(unlock()),但功能更多(比如可以设置 “排队规则”、“超时放弃”)。
核心优势(解决 synchronized 的痛点):
- synchronized 的问题:你排队等锁时,不能中途放弃,也不能被人叫走(不可中断);而 ReentrantLock 可以。
- 举个生活例子:你排队用保险柜,等了 3 分钟还没轮到,你可以选择放弃(超时);或者朋友叫你有急事,你可以中途离开(可中断)。
简单代码示例(一看就懂):
import java.util.concurrent.locks.ReentrantLock;
public class SafeBox {
// 初始化锁:true=公平锁(排队按顺序),false=非公平锁(默认,可能插队)
private final ReentrantLock lock = new ReentrantLock(true);
public void getMoney() {
try {
// 尝试开锁,最多等3秒(超时就放弃)
boolean gotLock = lock.tryLock(3, java.util.concurrent.TimeUnit.SECONDS);
if (gotLock) {
System.out.println("打开保险柜,取钱成功!"); // 临界区
} else {
System.out.println("等了3秒还没轮到,放弃取钱~");
}
} catch (InterruptedException e) {
System.out.println("中途被打断(比如有人叫我),放弃取钱~");
} finally {
// 必须手动关锁!(不然别人永远用不了)
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
什么时候用?
- 想控制 “排队规则”(比如必须按顺序,不能插队 → 公平锁)。
- 不想死等(比如等 5 秒没拿到锁就放弃)。
- 中途可能需要中断(比如线程被唤醒去做别的事)。
3. 读多写少专用:读写锁(ReentrantReadWriteLock)
相当于「图书馆的书架」——
- 读操作(看书):多个人可以同时看(共享锁),不冲突。
- 写操作(修改书的内容):只能一个人改(排他锁),别人不能读也不能写。
生活场景:
图书馆里,10 个人可以同时看同一本书(读锁共享);但如果有人要修改这本书的内容(写锁),必须等所有人都看完,他一个人改完后,别人才能再看。
简单代码示例:
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Library {
// 读写锁:读锁和写锁是一对
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); // 读锁
private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); // 写锁
private String bookContent = "初始内容"; // 共享资源(书的内容)
// 读操作(多人可同时读)
public String readBook() {
readLock.lock(); // 加读锁
try {
System.out.println("有人在看书,内容:" + bookContent);
return bookContent;
} finally {
readLock.unlock(); // 释放读锁
}
}
// 写操作(只能一人写)
public void writeBook(String newContent) {
writeLock.lock(); // 加写锁
try {
bookContent = newContent;
System.out.println("有人修改了书的内容:" + newContent);
} finally {
writeLock.unlock(); // 释放写锁
}
}
}
什么时候用?
- 读操作特别多,写操作特别少(比如缓存、配置文件读取)。
- 比如:1000 个线程读数据,1 个线程更新数据 → 用读写锁比 synchronized 快 100 倍(因为读不用排队)。
4. 无锁方案:volatile + 原子类(不用抢锁,更高效)
有些场景不用 “锁”,也能保证安全,相当于「不用排队的共享资源」。
(1)volatile:简单的 “状态通知”
相当于「公司的公告栏」—— 有人更新了公告(写线程改变量),所有人都能立刻看到(读线程拿到最新值),但不能保证 “同时改” 的安全。
适用场景:
只有一个人写,多个人读(比如 “是否停止” 的标记)。
public class NoticeBoard {
// volatile 保证:写了之后,所有人都能立刻看到
private volatile boolean isStop = false;
// 一个人写(更新公告)
public void setStop() {
isStop = true;
System.out.println("公告:停止工作!");
}
// 多个人读(看公告)
public void work() {
while (!isStop) { // 每次都能拿到最新的 isStop 值
System.out.println("正在工作...");
}
}
}
(2)原子类(AtomicInteger 等):简单的 “计数”
相当于「自动计数的投票箱」—— 多人同时投票(加 1),不会数错,不用排队(底层用 CAS 机制,相当于 “先看有没有人改,没人改再自己改”)。
适用场景:
简单的加减计数(比如统计访问量)。
import java.util.concurrent.atomic.AtomicInteger;
public class VoteBox {
// 原子类:自带“原子操作”,不用锁
private final AtomicInteger voteCount = new AtomicInteger(0);
// 多人同时投票(count++),不会数错
public void vote() {
voteCount.incrementAndGet(); // 相当于 count++,但线程安全
System.out.println("当前票数:" + voteCount.get());
}
}
三、怎么选锁?(一句话总结)
| 场景(生活类比) | 选什么锁 / 机制 | 一句话理由 |
|---|---|---|
| 简单抢资源(比如抢打印机) | synchronized | 不用手动开关,新手首选 |
| 想排队按顺序 / 不想死等 / 要中断 | ReentrantLock | 灵活,能解决 synchronized 的痛点 |
| 读多写少(比如图书馆看书) | 读写锁 / StampedLock | 读不用排队,效率高 |
| 单写多读(比如看公告) | volatile | 不用锁,简单高效 |
| 简单计数(比如投票) | 原子类(AtomicInteger) | 不用锁,不会数错 |
四、避坑小技巧(新手必看)
- 用 ReentrantLock 一定要在
finally里关锁(不然你忘了关,别人永远用不了)。 - 别嵌套锁(比如先锁 A 再锁 B,又先锁 B 再锁 A)→ 容易造成死锁(两个人互相拿着对方要的钥匙,都动不了)。
- 锁的范围越小越好(比如只锁 “复印” 功能,别把整个打印机都锁了)→ 别人还能同时用其他功能,效率高。
这样是不是好懂多了?核心就是「根据场景选规则」,不用死记概念,记住生活类比就行~
995

被折叠的 条评论
为什么被折叠?



