直接上案例

/**
 * 餐厅点餐系统演示
 * 这个程序模拟了一个简单餐厅的运作流程,包含:
 * 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()方法执行完毕或抛出异常

状态转换关系

  1. NEW → RUNNABLE

    • 调用start()方法

  2. RUNNABLE ←→ BLOCKED

    • 等待synchronized锁

    • 获得锁返回RUNNABLE

  3. RUNNABLE ←→ WAITING

    • 调用wait()/join()

    • 被notify()/notifyAll()唤醒

  4. RUNNABLE ←→ TIMED_WAITING

    • 调用sleep(time)/wait(time)

    • 时间到或被唤醒

  5. 任何状态 → TERMINATED

    • 线程执行完毕

    • 出现未处理异常


sleep防止CPU百分百?为什么?

核心原理

  1. sleep 会让线程暂时释放 CPU 时间片

  2. 其他线程因此可以获得执行机会

  3. 避免了单个线程一直占用 CPU

对比示例

// CPU 100%
while(true) {
    doSomething();
}

// CPU 使用率降低
while(true) {
    doSomething();
    Thread.sleep(100);  // 关键:让出 CPU 时间片
}

注意点

  • sleep 时间要适中,太长影响响应,太短无效果

  • 现代应用推荐用 ScheduledExecutorService 等工具类


守护线程与用户线程的区别

核心区别

  1. 生命周期

  • 用户线程:主线程结束后,用户线程会继续运行直到完成

  • 守护线程:当所有用户线程结束时,守护线程会被强制终止

  1. 设置方式

Thread thread = new Thread();
thread.setDaemon(true);  // 必须在start()之前设置
  1. 应用场景

  • 用户线程:执行业务代码,如处理用户请求

  • 守护线程:后台支持任务,如GC、内存监控、日志记录

  1. 优先级

  • 用户线程: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 + " 次");
    }
}

关键点解释

  1. volatile的作用

    • 确保isRunning变量在多线程间的可见性

    • 一个线程修改值后,其他线程立即可见

    • 防止出现线程缓存导致的可见性问题

  2. 两种停止方式的配合

    isRunning = false;  // 方式1:标志位控制
    workThread.interrupt();  // 方式2:中断机制
    • 标志位:温和的停止方式

    • 中断:及时响应的停止方式

  3. 为什么用join()

    • 确保工作线程完全停止后才继续执行

    • 避免测试过早结束导致断言失败

  4. 断言的作用

    • assertTrue:验证线程确实执行了足够的次数

    • assertFalse:验证线程确实已经停止


lock与synchronized的区别

选择建议

  1. 使用synchronized当

    • 代码简单,就是普通的同步需求

    • 不需要特殊的锁功能

    • 不想操心锁的释放问题

  2. 使用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使用权

  • 仅是建议性的,不保证一定让出

  • 不释放任何锁

  • 使用较少

最佳实践

  1. 优先使用高级并发工具

    • 线程池(ExecutorService)

    • 显式锁(Lock)

    • 并发集合

    • 同步工具(CountDownLatch, Semaphore等)

  2. 避免过度依赖

    • 线程优先级

    • yield方法

    • 直接操作线程


为什么wait/notify在Object中?

Object类
    ├── 所有类的父类
    ├── 包含wait/notify等方法
    └── 原因:
        ├── 每个对象都能作为锁
        ├── 锁需要等待/通知机制
        └── 统一的线程协作方式

1. 核心原因:

  • Java中的每个对象都可以作为锁

  • 锁对象需要协调线程等待和唤醒

  • Object作为所有类的父类,保证所有对象都有等待/通知机制