核心概念速览

方法 所属类 是否释放锁 线程状态 用途
run() Thread N/A RUNNABLE 线程执行体
start() Thread N/A NEW→RUNNABLE 启动线程
wait() Object ✅ 释放 WAITING 等待通知
sleep() Thread ❌ 不释放 TIMED_WAITING 暂停执行
notify() Object ❌ 不释放 WAITING→RUNNABLE 唤醒单个
notifyAll() Object ❌ 不释放 WAITING→RUNNABLE 唤醒全部

详细对比分析

1. run() vs start()

run()方法

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getName());
        });
        
        // 直接调用run():在main线程中执行
        thread.run(); // 输出:当前线程:main
    }
}

特点

  • 普通方法调用,不启动新线程
  • 当前线程中同步执行
  • 可以重复调用

start()方法

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getName());
        });
        
        // 调用start():创建新线程执行
        thread.start(); // 输出:当前线程:Thread-0
    }
}

特点

  • 启动新线程,由JVM调用run()
  • 异步执行,立即返回
  • 只能调用一次(重复调用抛IllegalThreadStateException

start()源码分析

public synchronized void start() {
    // 检查线程状态
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    // 加入线程组
    group.add(this);

    boolean started = false;
    try {
        start0(); // native方法,创建新线程
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
        }
    }
}

private native void start0(); // JVM调用run()

2. wait() vs sleep()

wait()方法

public class WaitDemo {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程1获取锁");
                try {
                    System.out.println("线程1释放锁,进入等待");
                    lock.wait(); // 释放锁,进入WAITING
                    System.out.println("线程1被唤醒,重新获取锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程2获取锁");
                lock.notify(); // 唤醒线程1
                System.out.println("线程2释放锁");
            }
        }).start();
    }
}

特点

  • 属于Object类,必须在synchronized块中调用
  • 释放锁,让其他线程可以进入同步块
  • 进入WAITINGTIMED_WAITING状态
  • 被唤醒后需要重新竞争锁

sleep()方法

public class SleepDemo {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程1获取锁");
                try {
                    System.out.println("线程1休眠,但不释放锁");
                    Thread.sleep(2000); // 不释放锁,进入TIMED_WAITING
                    System.out.println("线程1醒来,仍持有锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程2获取锁"); // 需要等待线程1 sleep结束
            }
        }).start();
    }
}

特点

  • 属于Thread类,可在任何地方调用
  • 不释放锁,继续持有
  • 进入TIMED_WAITING状态
  • 时间到期自动唤醒,无需其他线程通知

核心区别

特性 wait() sleep()
所属类 Object Thread
锁处理 释放锁 不释放锁
使用场景 synchronized块内 任何地方
唤醒方式 notify/notifyAll 超时自动
异常 InterruptedException InterruptedException

3. notify() vs notifyAll()

notify()方法

public class NotifyDemo {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 启动3个等待线程
        for (int i = 1; i <= 3; i++) {
            int finalI = i;
            new Thread(() -> {
                synchronized (lock) {
                    System.out.println("线程" + finalI + "开始等待");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程" + finalI + "被唤醒");
                }
            }).start();
        }

        Thread.sleep(1000);

        // 只唤醒一个线程
        synchronized (lock) {
            lock.notify(); // 随机唤醒1个线程
        }
    }
}

输出示例

线程1开始等待
线程2开始等待
线程3开始等待
线程2被唤醒  // 只有1个线程被唤醒

特点

  • 随机唤醒一个等待的线程
  • 其他线程继续等待
  • 性能更好,但可能导致线程饥饿

notifyAll()方法

public class NotifyAllDemo {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 启动3个等待线程
        for (int i = 1; i <= 3; i++) {
            int finalI = i;
            new Thread(() -> {
                synchronized (lock) {
                    System.out.println("线程" + finalI + "开始等待");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程" + finalI + "被唤醒");
                }
            }).start();
        }

        Thread.sleep(1000);

        // 唤醒所有线程
        synchronized (lock) {
            lock.notifyAll(); // 唤醒所有等待的线程
        }
    }
}

输出示例

线程1开始等待
线程2开始等待
线程3开始等待
线程1被唤醒  // 所有线程都被唤醒
线程2被唤醒
线程3被唤醒

特点

  • 唤醒所有等待的线程
  • 线程需要重新竞争锁
  • 避免线程饥饿,但性能开销更大

选择建议

// 场景1:单一条件,使用notify()
class SingleCondition {
    private boolean ready = false;
    private final Object lock = new Object();

    public void waitForReady() throws InterruptedException {
        synchronized (lock) {
            while (!ready) {
                lock.wait();
            }
        }
    }

    public void setReady() {
        synchronized (lock) {
            ready = true;
            lock.notify(); // 只需唤醒一个
        }
    }
}

// 场景2:多个条件,使用notifyAll()
class MultiCondition {
    private int count = 0;
    private final Object lock = new Object();

    public void waitForCondition(int target) throws InterruptedException {
        synchronized (lock) {
            while (count < target) {
                lock.wait();
            }
        }
    }

    public void increment() {
        synchronized (lock) {
            count++;
            lock.notifyAll(); // 需要唤醒所有,让它们重新检查条件
        }
    }
}

推荐

  • 默认使用notifyAll(),避免死锁和饥饿
  • 性能敏感且确保只有一个等待者时用notify()

线程安全与最佳实践

1. wait()必须在循环中使用

// ❌ 错误用法
synchronized (lock) {
    if (!condition) {
        lock.wait(); // 可能虚假唤醒
    }
}

// ✅ 正确用法
synchronized (lock) {
    while (!condition) {
        lock.wait(); // 防止虚假唤醒
    }
}

原因:防止虚假唤醒(Spurious Wakeup)

2. 避免使用wait/notify,推荐Lock + Condition

// 现代写法
class ModernWaitNotify {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void await() throws InterruptedException {
        lock.lock();
        try {
            condition.await(); // 替代wait()
        } finally {
            lock.unlock();
        }
    }

    public void signal() {
        lock.lock();
        try {
            condition.signal(); // 替代notify()
        } finally {
            lock.unlock();
        }
    }
}

优势

  • 支持多个Condition,更灵活
  • 可中断、可超时
  • 更清晰的语义

答题总结

面试标准答案

run() vs start()

  • run():普通方法,当前线程同步执行
  • start():启动新线程,JVM异步调用run()

wait() vs sleep()

  • wait():Object方法,释放锁,等待notify唤醒
  • sleep():Thread方法,不释放锁,超时自动唤醒

notify() vs notifyAll()

  • notify():随机唤醒一个,可能饥饿
  • notifyAll():唤醒全部,安全但性能略差

关键记忆点

  1. wait/notify/notifyAll属于Object,必须在synchronized块中
  2. sleep属于Thread,可在任何地方调用
  3. wait释放锁sleep不释放锁
  4. wait需要notify唤醒sleep自动唤醒
  5. 生产环境推荐Lock + Condition,替代wait/notify