引言:
1.如果你有一个厨房 如何安排厨师有序工作?
1. synchronized 关键字 -> 厨房里的令牌
想象厨房里有一个令牌,只有拿到令牌的厨师才能使用某个灶台(临界区)。其他厨师必须等待这个厨师用完令牌才能拿到令牌使用灶台。
例子:
public class Kitchen {
private int mealsPrepared = 0;
// 同步方法:就像厨师进入一个独立的厨房小隔间,每次只允许一个厨师进入
public synchronized void prepareMeal() {
// 准备餐点
mealsPrepared++;
}
// 同步代码块:只有拿到令牌(lock对象)的厨师才能执行代码块
private final Object lock = new Object();
public void prepareMealWithBlock() {
synchronized(lock) {
mealsPrepared++;
}
}
}
2. Lock 接口 -> 更灵活的令牌
Lock就像是一个更智能的令牌,可以设置等待时间,或者可中断的等待。
例子:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Kitchen {
private int mealsPrepared = 0;
private final Lock lock = new ReentrantLock();
public void prepareMeal() {
lock.lock(); // 申请令牌
try {
mealsPrepared++;
} finally {
lock.unlock(); // 无论怎样都要归还令牌
}
}
// 尝试获取令牌,如果等不及1秒就离开
public boolean tryPrepareMeal() {
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
mealsPrepared++;
return true;
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
}
3. volatile 关键字 -> 公告板
volatile变量就像厨房里的一个公告板,任何厨师更新了公告板(写操作),其他厨师立即能看到最新信息。但是注意,公告板只能保证可见性,不能保证复合操作的原子性。
例子:
public class Kitchen {
private volatile boolean stopCooking = false;
// 一个厨师设置停止烹饪
public void setStopCooking() {
stopCooking = true;
}
// 其他厨师检查是否停止
public void checkStop() {
if (stopCooking) {
// 停止当前烹饪
}
}
}
4. 原子类 -> 自动化的计数器
原子类就像是一个自动化的计数器,多个厨师同时点击计数器,计数器会正确增加,不会出现两个厨师点击一次却只增加一次的情况。
例子:
import java.util.concurrent.atomic.AtomicInteger;
public class Kitchen {
private AtomicInteger mealsPrepared = new AtomicInteger(0);
public void prepareMeal() {
mealsPrepared.incrementAndGet(); // 原子操作,多个厨师同时调用也不会错
}
}
5. 线程安全集合 -> 专用的储物架
ConcurrentHashMap就像是一个有多层抽屉的储物架,多个厨师可以同时从不同的抽屉拿取或存放食材,而不会互相阻塞。
例子:
import java.util.concurrent.ConcurrentHashMap;
public class Kitchen {
private ConcurrentHashMap<String, Integer> inventory = new ConcurrentHashMap<>();
public void addIngredient(String item, int quantity) {
inventory.put(item, quantity);
}
// 多个厨师可以同时调用这些方法而不会互相阻塞
public int getQuantity(String item) {
return inventory.getOrDefault(item, 0);
}
}
6. ThreadLocal -> 每人一套厨具
ThreadLocal就像给每个厨师分配一套独立的厨具,每个厨师只用自己那套,不会和别人争抢。
例子:
public class Kitchen {
private static ThreadLocal<Utensil> utensilHolder = new ThreadLocal<>();
public void setUtensil(Utensil utensil) {
utensilHolder.set(utensil);
}
public void useUtensil() {
Utensil utensil = utensilHolder.get();
// 使用厨具
}
}
7. 不可变对象 -> 一次性使用的食材
不可变对象就像是一次性使用的食材包,一旦包装好,里面的食材就不能改变,所以多个厨师可以同时读取这个食材包,而不用担心被别人修改。
例子:
public final class MealPackage {
private final String mainCourse;
private final String dessert;
public MealPackage(String mainCourse, String dessert) {
this.mainCourse = mainCourse;
this.dessert = dessert;
}
// 只有getter,没有setter
public String getMainCourse() { return mainCourse; }
public String getDessert() { return dessert; }
}
8. 并发工具类 -> 厨房里的协调员
CountDownLatch:就像一个任务计数器,完成一个任务就减一,直到为零。
CyclicBarrier:就像一组厨师在某个步骤必须等待所有人都完成才能继续。
例子(CountDownLatch):
import java.util.concurrent.CountDownLatch;
public class Kitchen {
public void prepareMeal() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3); // 需要完成3个任务
// 三个厨师分别做不同任务
new Thread(() -> {
prepareAppetizer();
latch.countDown();
}).start();
new Thread(() -> {
prepareMainCourse();
latch.countDown();
}).start();
new Thread(() -> {
prepareDessert();
latch.countDown();
}).start();
latch.await(); // 等待所有任务完成
serveMeal();
}
}
例子(CyclicBarrier):
import java.util.concurrent.CyclicBarrier;
public class Kitchen {
public void prepareMeal() {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
// 当所有厨师都到达屏障时执行
serveMeal();
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
preparePartOfMeal();
try {
barrier.await(); // 等待其他厨师
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
}
总结
在厨房(多线程环境)中,我们要保证:
-
互斥:一个灶台一次只能一个厨师用(synchronized, Lock)
-
可见性:一个厨师更新了公告板,其他厨师立即看到(volatile)
-
原子性:操作不可中断,比如计数器增加(原子类)
-
线程封闭:每人用自己的厨具(ThreadLocal)
-
不可变:食材包一旦包装好就不变(不可变对象)
-
协调:多个厨师之间的协作(CountDownLatch, CyclicBarrier)
💡 实际选择建议
// 场景1:简单的计数器 → 用原子类
AtomicInteger visitorCount = new AtomicInteger(0);
// 场景2:复杂的数据结构操作 → 用锁
ReentrantLock dataLock = new ReentrantLock();
// 场景3:状态标志 → 用volatile
volatile boolean isRunning = true;
// 场景4:快速开发 → 用synchronized
public synchronized void quickMethod() { }
记住核心思想:当多个人要修改同一个东西时,要排队或者用原子操作!
1015

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



