直接上案例
/**
* 餐厅点餐系统演示
* 这个程序模拟了一个简单餐厅的运作流程,包含:
* 1. 顾客点餐
* 2. 厨师做菜
* 3. 顾客取餐
*
* 关键概念:
* - synchronized: 同步机制,确保同一时间只能有一个线程访问
* - wait(): 线程等待(比如顾客等待菜品)
* - notifyAll(): 通知所有等待的线程(比如通知顾客菜好了)
*/
public class RestaurantDemoTest {
/**
* 订单状态枚举
* 用来跟踪订单从创建到送达的整个过程
* 就像真实餐厅的点餐小票上的状态标记
*/
enum OrderStatus {
CREATED("已下单"), // 顾客刚点完餐
COOKING("制作中"), // 厨师正在做菜
READY("已完成"), // 菜品制作完成
DELIVERED("已送达"); // 顾客已取餐
private final String status;
OrderStatus(String status) {
this.status = status;
}
}
/**
* 订单类
* 就像餐厅的点餐小票,记录订单的所有信息
*/
static class Order {
private final int orderId; // 订单编号,用于跟踪订单
private final String dishName; // 菜品名称
private OrderStatus status; // 当前订单状态
private final String customerName; // 点餐的顾客名字
private final long createTime; // 订单创建时间
// 创建新订单时,初始化所有信息
public Order(int orderId, String dishName, String customerName) {
this.orderId = orderId;
this.dishName = dishName;
this.customerName = customerName;
this.status = OrderStatus.CREATED; // 初始状态为"已下单"
this.createTime = System.currentTimeMillis(); // 记录下单时间
}
}
/**
* 餐厅类 - 包含餐厅的核心运作逻辑
*/
static class Restaurant {
private Order currentOrder; // 当前正在处理的订单(相当于传菜窗口)
private int orderIdCounter = 1; // 订单号计数器,自动递增
private boolean isOpen = true; // 餐厅是否营业
/**
* 顾客点餐方法
* synchronized 确保同一时间只能有一个顾客点餐
*/
public synchronized void placeOrder(String dishName) {
// 获取当前顾客的名字(从线程名称中获取)
String customerName = Thread.currentThread().getName();
try {
// 如果有其他顾客的订单正在处理,就需要等待
// 这就像排队等位一样
while (currentOrder != null && isOpen) {
printLog(customerName, "前面还有顾客在用餐,请稍等...");
wait(); // 等待其他顾客用餐完毕
}
// 餐厅打烊检查
if (!isOpen) {
printLog(customerName, "抱歉,餐厅已打烊");
return;
}
// 创建新订单(就像服务员记录点单)
currentOrder = new Order(orderIdCounter++, dishName, customerName);
printLog(customerName, String.format("点了一份%s [订单号:%d]",
dishName, currentOrder.orderId));
notifyAll(); // 通知厨师有新订单
// 等待菜品制作完成
// 这就像坐在位置上等待上菜
while (currentOrder != null &&
currentOrder.status != OrderStatus.READY &&
isOpen) {
printLog(customerName, String.format("等待%s制作完成 [订单号:%d]",
dishName, currentOrder.orderId));
wait(); // 等待厨师通知
}
// 如果菜品已完成且餐厅还在营业
if (currentOrder != null && isOpen) {
// 取餐并享用
printLog(customerName, String.format("%s真香!开动啦![订单号:%d]",
dishName, currentOrder.orderId));
currentOrder.status = OrderStatus.DELIVERED;
currentOrder = null; // 清空当前订单
notifyAll(); // 通知其他人(下一位顾客可以点餐了)
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
printLog(customerName, "用餐被中断");
}
}
/**
* 厨师做菜方法
* 不断循环检查是否有新订单需要处理
*/
public synchronized void cook() {
String chefName = Thread.currentThread().getName();
try {
while (isOpen) { // 餐厅营业时间内持续工作
// 如果没有订单或当前订单已完成,就等待新订单
while (currentOrder == null ||
currentOrder.status == OrderStatus.READY) {
if (!isOpen) return; // 餐厅关门就下班
printLog(chefName, "没有订单,休息一下...");
wait(); // 等待新订单
}
// 开始做菜
currentOrder.status = OrderStatus.COOKING;
printLog(chefName, String.format("开始制作%s [订单号:%d]",
currentOrder.dishName, currentOrder.orderId));
Thread.sleep(2000); // 模拟做菜时间(2秒)
// 完成菜品
currentOrder.status = OrderStatus.READY;
printLog(chefName, String.format("%s制作完成![订单号:%d]",
currentOrder.dishName, currentOrder.orderId));
notifyAll(); // 通知顾客取餐
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
printLog(chefName, "工作被中断");
}
}
/**
* 关闭餐厅
* 通知所有人餐厅要打烊了
*/
public synchronized void closeRestaurant() {
isOpen = false;
notifyAll(); // 通知所有等待的线程(顾客和厨师)
printLog("系统", "餐厅打烊了");
}
/**
* 日志输出工具方法
* 按统一格式打印日志信息
*/
private void printLog(String name, String message) {
System.out.printf("【%s】%s: %s\n",
LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
name, message);
}
}
/**
* 测试方法
* 模拟真实餐厅运营场景
*/
@Test
public void testRestaurant() {
/*
* 顾客到店 -> 检查是否有人在用餐 ->
* 有人在用餐 -> 等待(wait)
* 没人在用餐 -> 点餐 -> 通知厨师(notify) ->
* 等待菜品(wait) ->
* 取餐用餐 -> 通知下一位(notify)
*/
Restaurant restaurant = new Restaurant();
// 创建并启动厨师线程
Thread chef = new Thread(restaurant::cook, "小张师傅");
// 创建并启动顾客线程
Thread customer1 = new Thread(() -> restaurant.placeOrder("宫保鸡丁"), "张三");
Thread customer2 = new Thread(() -> restaurant.placeOrder("糖醋里脊"), "李四");
// 所有人开始工作
chef.start(); // 厨师开始工作
customer1.start(); // 第一位顾客进店
customer2.start(); // 第二位顾客进店
// 模拟餐厅营业时间
try {
Thread.sleep(8000); // 营业8秒
restaurant.closeRestaurant(); // 餐厅打烊
chef.join(2000); // 等待厨师处理完最后的订单
customer1.join(); // 等待顾客1用餐完毕
customer2.join(); // 等待顾客2用餐完毕
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
join方法底层的设计原理是什么样的?
多线程底层六种状态
1. NEW(新建)
线程被创建但还没有调用start()方法
这时线程只是一个空壳,还未分配资源
例如:Thread thread = new Thread(); // 这时就是NEW状态
2. RUNNABLE(可运行)
包含了两个子状态:READY(就绪)和RUNNING(运行中)
READY:等待CPU分配时间片
RUNNING:正在CPU上运行
调用了thread.start()后就进入RUNNABLE状态
可能在运行,也可能在等待CPU时间片
3. BLOCKED(阻塞)
线程被锁阻塞,等待获取synchronized锁
等待进入同步代码块或同步方法
情况如:
- 等待进入synchronized方法/代码块
- 其他线程正持有该锁
4. WAITING(等待)
线程无限期等待,直到被其他线程唤醒
调用以下方法会进入该状态:
Object.wait()
Thread.join()
LockSupport.park()
需要其他线程调用notify()/notifyAll()来唤醒
5.TIMED_WAITING(限时等待)
在指定时间内等待
调用以下方法会进入该状态:
Thread.sleep(time)
Object.wait(time)
Thread.join(time)
LockSupport.parkNanos(time)
LockSupport.parkUntil(time)
到达指定时间后自动返回
6. TERMINATED(终止)
线程运行完成
或因异常而终止
run()方法执行完毕或抛出异常
状态转换关系:
NEW → RUNNABLE
调用start()方法
RUNNABLE ←→ BLOCKED
等待synchronized锁
获得锁返回RUNNABLE
RUNNABLE ←→ WAITING
调用wait()/join()
被notify()/notifyAll()唤醒
RUNNABLE ←→ TIMED_WAITING
调用sleep(time)/wait(time)
时间到或被唤醒
任何状态 → TERMINATED
线程执行完毕
出现未处理异常
sleep防止CPU百分百?为什么?
核心原理:
sleep 会让线程暂时释放 CPU 时间片
其他线程因此可以获得执行机会
避免了单个线程一直占用 CPU
对比示例:
// CPU 100%
while(true) {
doSomething();
}
// CPU 使用率降低
while(true) {
doSomething();
Thread.sleep(100); // 关键:让出 CPU 时间片
}
注意点:
sleep 时间要适中,太长影响响应,太短无效果
现代应用推荐用 ScheduledExecutorService 等工具类
守护线程与用户线程的区别
核心区别:
生命周期:
用户线程:主线程结束后,用户线程会继续运行直到完成
守护线程:当所有用户线程结束时,守护线程会被强制终止
设置方式:
Thread thread = new Thread();
thread.setDaemon(true); // 必须在start()之前设置
应用场景:
用户线程:执行业务代码,如处理用户请求
守护线程:后台支持任务,如GC、内存监控、日志记录
优先级:
用户线程:JVM会等待用户线程执行完成
守护线程:JVM不会等待守护线程
注意事项:
setDaemon()必须在start()前调用
守护线程创建的子线程也是守护线程
不能靠守护线程完成重要业务
如何安全的停止一个线程?
/**
* 线程安全停止的测试类
* @author uluckyXH
* @date 2024-12-25
*/
public class ThreadStopTest {
// volatile关键字确保多线程间的可见性
// 当一个线程修改了这个变量的值,其他线程能立即看到修改后的值
private volatile boolean isRunning = true;
// 计数器,记录线程执行的次数
private int counter = 0;
@Test
public void testSafeThreadStop() throws InterruptedException {
// 创建工作线程,使用lambda表达式简化线程创建
Thread workThread = new Thread(() -> {
while (isRunning) { // 通过检查标志位决定是否继续执行
try {
// 记录并打印线程执行的次数
System.out.println("线程执行次数: " + (++counter));
// 模拟线程执行任务,休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
// 当线程被中断时,打印信息并通过break结束循环
System.out.println("线程被中断");
break;
}
}
System.out.println("线程安全停止");
});
// 启动工作线程
workThread.start();
// 主线程休眠3秒,让工作线程有足够时间执行
Thread.sleep(3000);
// 停止线程的两种方式配合使用:
// 1. 修改标志位
isRunning = false;
// 2. 发送中断信号
workThread.interrupt();
// 等待工作线程完全终止
// join()会阻塞当前线程(主线程),直到工作线程执行完毕
workThread.join();
// 使用断言验证线程行为是否符合预期
assertTrue("线程应该至少执行3次", counter >= 3);
assertFalse("线程应该已经停止", workThread.isAlive());
System.out.println("测试完成,线程执行了 " + counter + " 次");
}
}
关键点解释:
volatile的作用:
确保isRunning变量在多线程间的可见性
一个线程修改值后,其他线程立即可见
防止出现线程缓存导致的可见性问题
两种停止方式的配合:
isRunning = false; // 方式1:标志位控制 workThread.interrupt(); // 方式2:中断机制
标志位:温和的停止方式
中断:及时响应的停止方式
为什么用join():
确保工作线程完全停止后才继续执行
避免测试过早结束导致断言失败
断言的作用:
assertTrue:验证线程确实执行了足够的次数
assertFalse:验证线程确实已经停止
lock与synchronized的区别
选择建议:
使用synchronized当:
代码简单,就是普通的同步需求
不需要特殊的锁功能
不想操心锁的释放问题
使用Lock当:
需要尝试获取锁(tryLock)
需要可中断的获取锁
需要超时功能
需要公平锁
需要多个条件变量
生活中的比喻:
synchronized就像自动门:进出都是自动的,不会忘记关门
Lock就像手动门:灵活但要记得关门,忘记关门就麻烦了
案例
/**
* Lock锁的测试案例 - 使用JUnit 5
* @author uluckyXH
* @date 2024-12-25
*/
@DisplayName("Lock锁测试类")
public class LockTest {
// 共享资源:账户余额
private int balance;
// 用于记录成功取款的次数
private AtomicInteger successCount;
// 创建可重入锁
private final Lock lock = new ReentrantLock();
@BeforeEach
void setUp() {
balance = 1000; // 初始余额1000
successCount = new AtomicInteger(0);
}
/**
* 使用Lock的取款方法
*/
public boolean withdrawWithLock(int amount) {
lock.lock(); // 获取锁
try {
// 模拟网络延迟
Thread.sleep(100);
if (balance >= amount) {
balance -= amount;
successCount.incrementAndGet();
System.out.println(Thread.currentThread().getName() +
" 取款成功,金额: " + amount + ", 余额:" + balance);
return true;
}
System.out.println(Thread.currentThread().getName() +
" 余额不足,取款失败");
return false;
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断标志
return false;
} finally {
lock.unlock();
}
}
/**
* 展示Lock的高级特性 - tryLock带超时的获取锁
*/
public boolean withdrawWithTryLock(int amount) {
try {
// 尝试获取锁,等待1秒
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// 模拟网络延迟
Thread.sleep(100);
if (balance >= amount) {
balance -= amount;
successCount.incrementAndGet();
System.out.println(Thread.currentThread().getName() +
" 取款成功,金额: " + amount + ", 余额:" + balance);
return true;
}
System.out.println(Thread.currentThread().getName() +
" 余额不足,取款失败");
return false;
} finally {
lock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() +
" 获取锁超时,取款失败");
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName() +
" 操作被中断");
return false;
}
}
@Test
@DisplayName("测试Lock普通取款功能")
void testLockWithdraw() throws InterruptedException {
int threadCount = 5;
CountDownLatch latch = new CountDownLatch(threadCount);
// 创建多个线程同时取款300元
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
withdrawWithLock(300);
} finally {
latch.countDown();
}
}, "Thread-" + i).start();
}
// 等待所有线程完成
latch.await();
// 验证结果
System.out.println("最终余额:" + balance);
System.out.println("成功取款次数:" + successCount.get());
// 使用JUnit 5的断言
assertAll(
() -> assertTrue(balance >= 0, "余额不应该为负数"),
() -> assertEquals(3, successCount.get(), "成功取款次数应该为3"),
() -> assertEquals(100, balance, "最终余额应该为100")
);
}
@Test
@DisplayName("测试tryLock超时取款功能")
void testTryLockWithdraw() throws InterruptedException {
int threadCount = 5;
CountDownLatch latch = new CountDownLatch(threadCount);
// 创建多个线程同时尝试取款300元
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
withdrawWithTryLock(300);
} finally {
latch.countDown();
}
}, "Thread-" + i).start();
}
// 等待所有线程完成
latch.await();
// 验证结果
System.out.println("最终余额:" + balance);
System.out.println("成功取款次数:" + successCount.get());
// 使用JUnit 5的断言
assertAll(
() -> assertTrue(balance >= 0, "余额不应该为负数"),
() -> assertTrue(successCount.get() <= 3,
"成功取款次数应该小于等于3: " + successCount.get())
);
}
}
lock的condition用法
/**
* 餐厅点餐系统模拟
* - 服务员负责点单(生产者)
* - 厨师负责做菜(消费者)
* - 订单队列有限制(最多同时处理3个订单)
*/
public class RestaurantTest {
static class Restaurant {
// 存储订单的队列
private final Queue<String> orderQueue = new LinkedList<>();
// 最大同时处理的订单数
private final int maxOrders;
// 餐厅的门锁(ReentrantLock)
private final Lock lock = new ReentrantLock();
// 订单队列未满条件(服务员等待)
private final Condition notFull = lock.newCondition();
// 订单队列非空条件(厨师等待)
private final Condition notEmpty = lock.newCondition();
public Restaurant(int maxOrders) {
this.maxOrders = maxOrders;
}
/**
* 服务员接单方法
* @param orderNo 订单编号
*/
public void takeOrder(String orderNo) throws InterruptedException {
lock.lock(); // 服务员进入厨房前先上锁
try {
// 如果订单队列满了,服务员需要等待
while (orderQueue.size() == maxOrders) {
System.out.printf("【%s】订单队列已满(%d),服务员等待...\n",
Thread.currentThread().getName(), maxOrders);
notFull.await(); // 等待队列不满
}
// 添加订单到队列
orderQueue.offer(orderNo);
System.out.printf("【%s】添加新订单: %s, 当前订单数: %d\n",
Thread.currentThread().getName(), orderNo, orderQueue.size());
// 通知厨师有新订单
notEmpty.signal();
} finally {
lock.unlock(); // 服务员离开厨房,解锁
}
}
/**
* 厨师做菜方法
* @return 订单编号
*/
public String cookOrder() throws InterruptedException {
lock.lock(); // 厨师进入厨房前先上锁
try {
// 如果没有订单,厨师需要等待
while (orderQueue.isEmpty()) {
System.out.printf("【%s】没有订单,厨师等待中...\n",
Thread.currentThread().getName());
notEmpty.await(); // 等待队列非空
}
// 取出订单开始做菜
String orderNo = orderQueue.poll();
System.out.printf("【%s】正在制作订单: %s, 剩余订单数: %d\n",
Thread.currentThread().getName(), orderNo, orderQueue.size());
// 通知服务员可以继续接单
notFull.signal();
return orderNo;
} finally {
lock.unlock(); // 厨师离开厨房,解锁
}
}
public int getCurrentOrders() {
lock.lock();
try {
return orderQueue.size();
} finally {
lock.unlock();
}
}
}
@Test
@DisplayName("测试餐厅订单系统")
void testRestaurant() throws InterruptedException {
// 创建一个最多同时处理3个订单的餐厅
Restaurant restaurant = new Restaurant(3);
// 计数器:2个服务员和2个厨师
CountDownLatch latch = new CountDownLatch(4);
// 创建2个服务员线程
for (int i = 1; i <= 2; i++) {
final int waiterId = i;
new Thread(() -> {
try {
// 每个服务员接3个订单
for (int j = 1; j <= 3; j++) {
String orderNo = String.format("订单-%d-%d", waiterId, j);
restaurant.takeOrder(orderNo);
// 模拟接单时间
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown();
}
}, "服务员-" + i).start();
}
// 创建2个厨师线程
for (int i = 1; i <= 2; i++) {
new Thread(() -> {
try {
// 每个厨师制作3个订单
for (int j = 1; j <= 3; j++) {
String orderNo = restaurant.cookOrder();
Assertions.assertNotNull(orderNo, "订单不能为空");
// 模拟做菜时间
Thread.sleep(500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown();
}
}, "厨师-" + i).start();
}
// 等待所有人完成工作
latch.await();
// 确认所有订单都已处理完
Assertions.assertEquals(0, restaurant.getCurrentOrders(),
"所有订单都应该处理完毕");
}
}
多线程的yield方法
/**
* Thread.yield() 示例
* 把yield理解为"谦让"
* 场景:模拟两个运动员在跑道上跑步
*/
public class YieldExampleTest {
// 用于记录跑步进度
private static class Runner {
private int distance = 0;
private final String name;
public Runner(String name) {
this.name = name;
}
public void run() {
distance++;
}
public int getDistance() {
return distance;
}
public String getName() {
return name;
}
}
@Test
public void testYield() throws InterruptedException {
Runner politeRunner = new Runner("礼貌运动员");
Runner normalRunner = new Runner("普通运动员");
// 礼貌运动员线程:每跑10米就让别人先跑
Thread politeThread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
politeRunner.run();
if (i % 10 == 0) {
System.out.printf("【%s】跑了%d米,主动让出跑道\n",
politeRunner.getName(),
politeRunner.getDistance());
Thread.yield(); // 让出CPU
}
}
});
// 普通运动员线程:一直跑
Thread normalThread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
normalRunner.run();
System.out.printf("【%s】跑了%d米\n",
normalRunner.getName(),
normalRunner.getDistance());
}
});
// 开始比赛
System.out.println("=== 比赛开始 ===");
politeThread.start();
normalThread.start();
// 等待比赛结束
politeThread.join();
normalThread.join();
// 打印结果
System.out.println("\n=== 比赛结束 ===");
System.out.printf("礼貌运动员跑了:%d米\n", politeRunner.getDistance());
System.out.printf("普通运动员跑了:%d米\n", normalRunner.getDistance());
}
/**
* yield的实际应用示例:CPU密集型任务让出执行权
*/
@Test
public void testPracticalYield() throws InterruptedException {
Thread cpuIntensiveTask = new Thread(() -> {
long sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i;
if (i % 10000 == 0) {
Thread.yield(); // 定期让出CPU
}
}
System.out.println("计算密集型任务完成,结果:" + sum);
}, "计算线程");
Thread ioTask = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
// 模拟IO操作
Thread.sleep(100);
System.out.println("IO操作完成:" + i);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "IO线程");
cpuIntensiveTask.start();
ioTask.start();
cpuIntensiveTask.join();
ioTask.join();
}
}
多线程的优先级
/**
* 赛车线程优先级演示
* 模拟不同级别赛车的竞速
*/
public class RacingPriorityTest {
static class RaceCar implements Runnable {
private final String name; // 赛车名称
private final String level; // 赛车等级
private int distance = 0; // 行驶距离
private final int totalDistance; // 总距离
public RaceCar(String name, String level, int totalDistance) {
this.name = name;
this.level = level;
this.totalDistance = totalDistance;
}
@Override
public void run() {
while (distance < totalDistance) {
distance++;
// 每跑10米报告一次进度
if (distance % 10 == 0) {
System.out.printf("【%s】%s 已经跑了 %d 米\n",
level, name, distance);
}
// 模拟跑动
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
// 完成比赛
System.out.printf("【%s】%s 完成比赛!\n", level, name);
}
public int getDistance() {
return distance;
}
}
@Test
public void testRacingPriority() throws InterruptedException {
// 创建三辆不同级别的赛车
RaceCar f1Car = new RaceCar("红牛F1", "F1级别", 100);
RaceCar gtCar = new RaceCar("保时捷GT", "GT级别", 100);
RaceCar stockCar = new RaceCar("普通轿车", "普通级别", 100);
// 创建对应的线程
Thread f1Thread = new Thread(f1Car, "F1线程");
Thread gtThread = new Thread(gtCar, "GT线程");
Thread stockThread = new Thread(stockCar, "普通线程");
// 设置优先级
f1Thread.setPriority(Thread.MAX_PRIORITY); // 优先级 10
gtThread.setPriority(Thread.NORM_PRIORITY); // 优先级 5
stockThread.setPriority(Thread.MIN_PRIORITY); // 优先级 1
// 比赛开始
System.out.println("=== 比赛开始 ===");
System.out.println("赛车优先级说明:");
System.out.printf("F1赛车优先级: %d\n", f1Thread.getPriority());
System.out.printf("GT赛车优先级: %d\n", gtThread.getPriority());
System.out.printf("普通车优先级: %d\n", stockThread.getPriority());
// 启动所有线程
stockThread.start();
gtThread.start();
f1Thread.start();
// 等待比赛结束
stockThread.join();
gtThread.join();
f1Thread.join();
// 比赛结果
System.out.println("\n=== 比赛结束 ===");
System.out.println("最终成绩:");
System.out.printf("F1赛车跑了:%d米\n", f1Car.getDistance());
System.out.printf("GT赛车跑了:%d米\n", gtCar.getDistance());
System.out.printf("普通车跑了:%d米\n", stockCar.getDistance());
}
}
线程状态与控制总结
1. 线程的六种状态
NEW: 新建,线程被创建但还未启动
RUNNABLE: 就绪/运行,等待CPU或正在运行
BLOCKED: 阻塞,等待获取synchronized锁
WAITING: 等待,无限期等待其他线程唤醒
TIMED_WAITING: 限时等待,等待一定时间后自动唤醒
TERMINATED: 终止,线程完成或异常结束
2. 线程优先级
范围:1-10(MIN_PRIORITY=1, NORM_PRIORITY=5, MAX_PRIORITY=10)
特点:仅作为建议,不保证执行顺序
实践:很少使用,不同操作系统实现不一样
3. 线程控制方法对比
Sleep:
- 不释放任何锁
- 让线程暂停指定时间
- 到时间自动恢复
- 可以在任何地方使用
Wait:
- 必须在synchronized块中使用
- 会释放当前持有的对象锁
- 需要notify/notifyAll唤醒
- 被唤醒后需要重新竞争锁
Join:
- 等待目标线程完成
- 不会释放持有的其他对象的锁
- 如果持有了线程对象的锁会释放该锁
- 常用于线程协调
4. yield
让出CPU使用权
仅是建议性的,不保证一定让出
不释放任何锁
使用较少
最佳实践
优先使用高级并发工具
线程池(ExecutorService)
显式锁(Lock)
并发集合
同步工具(CountDownLatch, Semaphore等)
避免过度依赖
线程优先级
yield方法
直接操作线程
为什么wait/notify在Object中?
Object类
├── 所有类的父类
├── 包含wait/notify等方法
└── 原因:
├── 每个对象都能作为锁
├── 锁需要等待/通知机制
└── 统一的线程协作方式
1. 核心原因:
Java中的每个对象都可以作为锁
锁对象需要协调线程等待和唤醒
Object作为所有类的父类,保证所有对象都有等待/通知机制