
在 Java 多线程编程中,run()/start()、wait()/sleep()、notify()/notifyAll()是三组高频且易混淆的方法,它们的核心区别体目前设计目的、线程状态、锁机制、调用场景等方面,下面逐一详解:
一、run()vsstart()(线程启动相关)
两者均与线程执行逻辑相关,但本质是普通方法调用和新线程启动的区别。
|
维度 |
run()方法 |
start()方法 |
|
所属类 |
Thread类(或Runnable接口) |
Thread类 |
|
核心作用 |
定义线程的执行逻辑(业务代码) |
启动新线程,使线程进入就绪状态 |
|
是否创建新线程 |
否(直接调用时,在当前线程执行) |
是(JVM 会分配新线程,由 CPU 调度执行) |
|
调用次数 |
可多次调用(普通方法) |
仅能调用一次(多次调用抛 |
|
线程状态变化 |
无(当前线程保持原有状态) |
新建(New)→ 就绪(Runnable)→ 运行(Running) |
示例对比
public class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println("线程名:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
ThreadDemo t = new ThreadDemo();
// 直接调用run():在main线程执行,无新线程
t.run(); // 输出:线程名:main
// 调用start():启动新线程,由JVM调用run()
t.start(); // 输出:线程名:Thread-0
}
}
二、wait()vssleep()(线程暂停相关)
两者均能让线程暂停执行,但设计初衷、锁处理、使用场景完全不同。
|
维度 |
wait()方法 |
sleep(long millis)方法 |
|
所属类 |
Object类(所有对象都可调用) |
Thread类(静态方法) |
|
核心目的 |
线程间通信(等待某个条件满足) |
线程定时暂停(休眠指定时间) |
|
锁机制 |
会释放持有的对象锁(必须在同步块 / 方法中调用) |
不释放任何锁(可在任意位置调用) |
|
唤醒方式 |
需通过notify()/notifyAll()唤醒,或超时自动醒 |
超时自动醒,或interrupt()打断 |
|
异常处理 |
必须捕获InterruptedException |
必须捕获InterruptedException |
|
调用前提 |
必须在synchronized块 / 方法中(否则抛 |
无前提(可在任意代码块调用) |
示例对比:
// wait()释放锁的示例
public class WaitDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
try {
System.out.println("线程1:持有锁,进入wait()");
lock.wait(); // 释放lock锁,线程进入等待队列
System.out.println("线程1:被唤醒,重新获取锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
synchronized (lock) { // 能获取锁,由于线程1已释放
System.out.println("线程2:获取到锁,执行任务");
lock.notify(); // 唤醒等待的线程1
}
}).start();
}
}
// sleep()不释放锁的示例
public class SleepDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
try {
System.out.println("线程1:持有锁,进入sleep()");
Thread.sleep(2000); // 不释放lock锁
System.out.println("线程1:sleep结束,释放锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
synchronized (lock) { // 需等待线程1释放锁(sleep期间拿不到)
System.out.println("线程2:获取到锁,执行任务");
}
}).start();
}
}
三、notify()vsnotifyAll()(线程唤醒相关)
两者均用于唤醒等待在对象锁上的线程,属于Object类的方法,需在同步块 / 方法中调用。
|
维度 |
notify()方法 |
notifyAll()方法 |
|
唤醒数量 |
唤醒一个等待在该对象锁上的线程(JVM 随机选择) |
唤醒所有等待在该对象锁上的线程 |
|
竞争锁 |
被唤醒的线程需与其他就绪线程竞争锁 |
所有被唤醒的线程需竞争锁(只有一个能拿到) |
|
适用场景 |
等待队列中所有线程执行一样任务(唤醒一个即可) |
等待队列中线程执行不同任务(需唤醒所有判断条件) |
|
调用前提 |
必须在synchronized块 / 方法中 |
必须在synchronized块 / 方法中 |
示例对比:
public class NotifyDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
// 启动3个等待线程
for (int i = 0; i < 3; i++) {
new Thread(() -> {
synchronized (lock) {
try {
System.out.println(Thread.currentThread().getName() + ":进入等待");
lock.wait();
System.out.println(Thread.currentThread().getName() + ":被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "等待线程" + i).start();
}
// 唤醒线程
new Thread(() -> {
synchronized (lock) {
System.out.println("
唤醒线程:调用notify()");
lock.notify(); // 仅唤醒一个等待线程
// lock.notifyAll(); // 唤醒所有等待线程
}
}).start();
}
}
// notify()输出(仅一个线程被唤醒):
// 等待线程0:进入等待
// 等待线程1:进入等待
// 等待线程2:进入等待
// 唤醒线程:调用notify()
// 等待线程0:被唤醒
// notifyAll()输出(所有线程被唤醒):
// 等待线程0:进入等待
// 等待线程1:进入等待
// 等待线程2:进入等待
// 唤醒线程:调用notifyAll()
// 等待线程2:被唤醒
// 等待线程1:被唤醒
// 等待线程0:被唤醒
核心总结
- run()是业务逻辑,start()才是真正启动线程;
- wait()用于通信且释放锁,sleep()用于休眠且不释放锁;
- notify()唤醒一个线程,notifyAll()唤醒所有线程,均需竞争锁。




