一、join()方法
join()
是 Java 中 Thread
类的一个重要方法,用于让一个线程等待另一个线程执行完毕。调用 join()
方法的线程会进入阻塞状态,直到目标线程完成执行。
作用
- 线程同步:确保某个线程在其他线程完成后才继续执行。
- 顺序控制:用于控制多个线程的执行顺序。
方法重载
join()
方法有多个重载版本:
join()
:无限期等待,直到目标线程结束。join(long millis)
:最多等待millis
毫秒,超时后继续执行。join(long millis, int nanos)
:最多等待millis
毫秒加nanos
纳秒。
示例代码
public class JoinExample implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " is running - " + i);
try {
Thread.sleep(500); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new JoinExample(), "Thread-1");
Thread t2 = new Thread(new JoinExample(), "Thread-2");
//start()异步启动线程,不会等待主线程结束
t1.start();
//主线程调用join方法,等待t1线程执行完毕
t1.join(); // 主线程等待 t1 执行完毕
t2.start();
t2.join(); // 主线程等待 t2 执行完毕
System.out.println("All threads have finished execution.");
}
}
输出示例
Thread-1 is running - 0
Thread-1 is running - 1
Thread-1 is running - 2
Thread-1 is running - 3
Thread-1 is running - 4
Thread-2 is running - 0
Thread-2 is running - 1
Thread-2 is running - 2
Thread-2 is running - 3
Thread-2 is running - 4
All threads have finished execution.
关键点
- 阻塞调用线程:调用
join()
的线程会阻塞,直到目标线程结束。 - 超时机制:可以设置超时时间,避免无限期等待。
- 中断支持:如果调用
join()
的线程被中断,会抛出InterruptedException
。
package com.liusaidh.thread;
/**
* @author lzw
* @date 2025/2/21 21:35
* @description 作用:
*/
public class JoinInterruptException {
public static void main(String[] args) {
//获取到主线程
Thread mainThread = Thread.currentThread();
// 创建并启动一个工作线程
Thread workerThread = new Thread(() -> {
System.out.println("工作线程开始执行...");
try {
// 模拟工作线程执行任务
Thread.sleep(5000); // 工作线程睡眠 10 秒,模拟工作
} catch (InterruptedException e) {
System.out.println("工作线程被中断!");
}
System.out.println("工作线程执行完毕。");
});
workerThread.start();
// 创建并启动一个中断线程
Thread interrupterThread = new Thread(() -> {
try {
// 模拟延迟
Thread.sleep(2000); // 中断线程睡眠 2 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断主线程
mainThread.interrupt();
System.out.println("中断线程尝试中断主线程...");
});
interrupterThread.start();
// 主线程等待工作线程完成
try {
System.out.println("主线程等待工作线程完成...");
workerThread.join(); // 主线程调用 join(),等待 workerThread 完成
} catch (InterruptedException e) {
System.out.println("主线程在 join() 时被中断!");
}
System.out.println("主线程继续执行...");
}
}
工作线程开始执行...
主线程等待工作线程完成...
中断线程尝试中断主线程...
主线程在 join() 时被中断!
主线程继续执行...
工作线程执行完毕。
- 适用场景:适用于需要等待其他线程完成后再继续执行的场景。
注意事项
- 死锁风险:如果多个线程相互调用
join()
,可能导致死锁。
public class JoinDeadLockExample {
static Thread threadA;
static Thread threadB;
public static void main(String[] args) {
// 创建两个线程
threadA = new Thread(() -> {
try {
System.out.println("线程A开始执行...");
Thread.sleep(1000); // 模拟任务执行
System.out.println("线程A等待线程B完成...");
threadB.join(); // 线程A等待线程B完成
System.out.println("线程A执行完毕。");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadB = new Thread(() -> {
try {
System.out.println("线程B开始执行...");
Thread.sleep(1000); // 模拟任务执行
System.out.println("线程B等待线程A完成...");
threadA.join(); // 线程B等待线程A完成
System.out.println("线程B执行完毕。");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 启动线程
threadA.start();
threadB.start();
}
}
线程A开始执行...
线程B开始执行...
线程A等待线程B完成...
线程B等待线程A完成...
- 性能影响:过度使用
join()
可能导致线程阻塞,影响程序性能。
总结
join()
方法是一种简单有效的线程同步工具,用于确保线程执行的顺序性。合理使用 join()
可以避免复杂的线程间通信问题,但需注意死锁和性能问题。
二、yield()方法
yield()
方法是 Thread
类的一个静态方法,用于提示线程调度器当前线程愿意让出 CPU 资源,使其他具有相同或更高优先级的线程有机会运行。不过,yield()
并不保证当前线程会立即暂停,具体行为取决于操作系统的线程调度器。
主要特点:
- 提示作用:
yield()
只是一个提示,调度器可能会忽略它。 - 不释放锁:调用
yield()
时,线程不会释放已持有的锁。这意味着,如果一个线程在持有锁的情况下调用 yield(),其他等待该锁的线程仍然无法获取锁,可能会导致性能问题或死锁。
public class YieldExample2 {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
synchronized (lock) {
System.out.println("ThreadA 获取了锁");
// 模拟一些操作
for (int i = 0; i < 5; i++) {
System.out.println("ThreadA 正在执行操作 " + i + "并且调用了yield()方法");
Thread.yield(); // ThreadA 调用 yield(),但不会释放锁
}
System.out.println("ThreadA 释放了锁");
}
});
Thread threadB = new Thread(() -> {
synchronized (lock) {
System.out.println("ThreadB 获取了锁");
// 模拟一些操作
for (int i = 0; i < 5; i++) {
System.out.println("ThreadB 正在执行操作 " + i);
}
System.out.println("ThreadB 释放了锁");
}
});
threadA.start();
threadB.start();
}
}
- 适用场景:适用于需要让出 CPU 但不阻塞线程的场景。
示例代码:
public class YieldExample implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " is running - " + i);
if (i == 2) {
System.out.println(Thread.currentThread().getName() + " is yielding - " + i);
Thread.yield(); // 提示让出 CPU,让另外的线程去执行程序
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new YieldExample(), "Thread-1");
Thread t2 = new Thread(new YieldExample(), "Thread-2");
t1.start();
t2.start();
}
}
输出示例:
Thread-1 is running - 0
Thread-1 is running - 1
Thread-1 is running - 2
Thread-2 is running - 0
Thread-2 is running - 1
Thread-2 is running - 2
Thread-1 is running - 3
Thread-1 is running - 4
Thread-2 is running - 3
Thread-2 is running - 4
注意事项:
- 不可靠:
yield()
的效果依赖于调度器,不能保证线程一定会暂停。 - 调试工具:通常用于调试或测试,实际开发中较少使用。
总结:
yield()
方法用于提示线程让出 CPU,但不保证立即生效,适用于需要让出 CPU 但不阻塞线程的场景。
三、wait()方法
wait()
是 Java 中 Object
类的一个方法,用于多线程编程中的线程间协调。它通常与 notify()
或 notifyAll()
方法配合使用,实现线程的等待和唤醒机制。wait()
方法的主要作用是让当前线程释放锁并进入等待状态,直到其他线程调用 notify()
或 notifyAll()
唤醒它。
wait()
方法的作用
- 释放锁:调用
wait()
的线程会释放它持有的对象锁。 - 进入等待状态:线程进入等待队列,直到其他线程调用
notify()
或notifyAll()
唤醒它。 - 线程协作:用于实现线程间的协调和通信。
wait()
方法的重载
wait()
方法有多个重载版本:
wait()
:无限期等待,直到其他线程调用notify()
或notifyAll()
。wait(long timeout)
:最多等待timeout
毫秒,超时后自动唤醒。wait(long timeout, int nanos)
:最多等待timeout
毫秒加nanos
纳秒。
使用 wait()
的步骤
- 获取对象锁:在调用
wait()
之前,线程必须持有对象的锁(通常通过synchronized
块实现)。 - **调用 **
wait()
:释放锁并进入等待状态。 - 被唤醒后重新竞争锁:当其他线程调用
notify()
或notifyAll()
后,等待的线程会重新竞争锁,获取锁后继续执行。
示例代码
以下是一个典型的生产者-消费者模型,使用 wait()
和 notify()
实现线程间协作:
public class WaitNotifyExample {
private final Object lock = new Object();
private boolean itemAvailable = false;
// 生产者方法
public void produce() throws InterruptedException {
synchronized (lock) {
while (itemAvailable) {
//使用while 循环,是为了防止虚假唤醒
// 如果物品还未被消费,生产者等待
lock.wait();
}
System.out.println("Producing an item...");
itemAvailable = true;
lock.notify(); // 唤醒消费者
}
}
// 消费者方法
public void consume() throws InterruptedException {
synchronized (lock) {
while (!itemAvailable) {
// 如果物品还未生产,消费者等待
lock.wait();
}
System.out.println("Consuming the item...");
itemAvailable = false;
lock.notify(); // 唤醒生产者
}
}
public static void main(String[] args) {
WaitNotifyExample example = new WaitNotifyExample();
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
example.produce();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
example.consume();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
输出示例
Producing an item...
Consuming the item...
Producing an item...
Consuming the item...
Producing an item...
Consuming the item...
Producing an item...
Consuming the item...
Producing an item...
Consuming the item...
关键点
- 必须在同步块中调用:
wait()
和notify()
必须在synchronized
块中调用,否则会抛出IllegalMonitorStateException
。 - 释放锁:调用
wait()
后,线程会释放锁,其他线程可以获取锁并执行。 - 唤醒机制:
notify()
:随机唤醒一个等待的线程。notifyAll()
:唤醒所有等待的线程。
- 避免虚假唤醒:通常将
wait()
放在while
循环中,防止虚假唤醒(spurious wakeup)。
wait()
的常见问题
- 死锁:如果所有线程都在等待,且没有线程调用
notify()
或notifyAll()
,会导致死锁。 - 虚假唤醒:线程可能会在没有调用
notify()
或notifyAll()
的情况下被唤醒,因此需要将wait()
放在循环中检查条件。
Java 官方文档中对 wait() 的说明明确指出:
As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:
synchronized (obj) {
while (<condition does not hold>) {
obj.wait();
}
// Perform action appropriate to condition
}
这意味着,wait() 应该始终在 while 循环中使用,以确保正确处理虚假唤醒和条件检查。
- 超时机制:可以使用
wait(long timeout)
避免无限期等待。
总结
wait()
用于线程间协作,让线程释放锁并进入等待状态。- 必须与
synchronized
配合使用,且通常与notify()
或notifyAll()
一起使用。 - 适用于生产者-消费者模型、线程池等场景。
notify()
是 Java 中用于线程间通信的一个方法,属于 Object
类的一部分。它通常与 wait()
方法一起使用,用于在多线程环境中实现线程的协调和同步。
四、notify()方法
notify()
的作用
- 唤醒等待的线程:
- 当一个线程调用
notify()
时,它会唤醒在该对象的监视器(锁)上等待的单个线程。 - 如果有多个线程在等待,具体唤醒哪个线程是不确定的,由 JVM 决定。
- 当一个线程调用
- 与
wait()
配合使用:notify()
通常与wait()
一起使用。wait()
使当前线程进入等待状态,并释放对象的锁;notify()
则用于唤醒等待的线程。
- 必须在同步块或同步方法中使用:
notify()
和wait()
必须在synchronized
块或方法中调用,否则会抛出IllegalMonitorStateException
异常。- 因为wait是使当前线程进入等待的状态,并释放锁,所以他的前提是必须有锁;notify()的前提是必须持有对象锁,否则无法正确的唤醒等待的线程。
- notify() 和 wait() 是用于线程间通信的机制,通常用于多线程环境中共享资源的协调。为了确保线程安全,必须满足以下条件:
- 独占访问:在调用 wait() 或 notify() 时,线程必须持有对象的锁(即进入 synchronized 块或方法),以确保在操作共享资源时不会被其他线程干扰。
- 原子性:wait() 和 notify() 的操作必须是原子的,即在一个线程调用这些方法时,其他线程不能同时修改共享资源的状态。
如果不在 synchronized 块中调用 wait() 或 notify(),可能会导致以下问题:
1. 多个线程同时修改共享资源,导致数据不一致。
2. 线程在调用 wait() 时没有释放锁,或者在调用 notify() 时没有正确唤醒其他线程。
notify()
的使用场景
notify()
通常用于生产者-消费者模型或线程间的协作场景。例如:
- 生产者线程生产数据后,通知消费者线程消费。
- 消费者线程消费数据后,通知生产者线程继续生产。
代码示例
以下是一个简单的生产者-消费者模型,演示了 notify()
和 wait()
的使用:
public class ProducerConsumerExample {
private static final Object lock = new Object(); // 锁对象
private static boolean isProduced = false; // 标志位,表示是否有数据
public static void main(String[] args) {
Thread producer = new Thread(() -> {
for (int i = 0; i < 5; i++) {
produce(i);
}
}, "Producer");
Thread consumer = new Thread(() -> {
for (int i = 0; i < 5; i++) {
consume();
}
}, "Consumer");
producer.start();
consumer.start();
}
// 生产者方法
private static void produce(int value) {
synchronized (lock) {
// 如果已经有数据,等待消费者消费
while (isProduced) {
try {
lock.wait(); // 释放锁,进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 生产数据: " + value);
isProduced = true; // 设置标志位为 true
lock.notify(); // 唤醒消费者线程
}
}
// 消费者方法
private static void consume() {
synchronized (lock) {
// 如果没有数据,等待生产者生产
while (!isProduced) {
try {
lock.wait(); // 释放锁,进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 消费数据");
isProduced = false; // 设置标志位为 false
lock.notify(); // 唤醒生产者线程
}
}
}
代码解析
- **锁对象 **
lock
:lock
是一个共享对象,用于同步生产者和消费者线程。
- **标志位 **
isProduced
:isProduced
表示是否有数据可供消费。- 生产者生产数据后将其设置为
true
,消费者消费数据后将其设置为false
。
- 生产者逻辑:
- 如果
isProduced
为true
,表示数据还未被消费,生产者调用wait()
进入等待状态。 - 生产数据后,设置
isProduced
为true
,并调用notify()
唤醒消费者线程。
- 如果
- 消费者逻辑:
- 如果
isProduced
为false
,表示没有数据可供消费,消费者调用wait()
进入等待状态。 - 消费数据后,设置
isProduced
为false
,并调用notify()
唤醒生产者线程。
- 如果
输出示例
Producer 生产数据: 0
Consumer 消费数据
Producer 生产数据: 1
Consumer 消费数据
Producer 生产数据: 2
Consumer 消费数据
Producer 生产数据: 3
Consumer 消费数据
Producer 生产数据: 4
Consumer 消费数据
关键点
wait()
** 和notify()
必须在同步块中使用**:- 调用
wait()
和notify()
时,线程必须持有对象的锁,否则会抛出IllegalMonitorStateException
。
- 调用
notify()
** 唤醒单个线程**:- 如果有多个线程在等待,
notify()
只会唤醒其中一个线程(具体是哪个线程不确定)。
- 如果有多个线程在等待,
notifyAll()
** 唤醒所有线程**:- 如果需要唤醒所有等待的线程,可以使用
notifyAll()
。
- 如果需要唤醒所有等待的线程,可以使用
- 避免虚假唤醒:
- 使用
while
循环检查条件,而不是if
,以防止虚假唤醒(spurious wakeup)。
- 使用
notify()
和 notifyAll()
的区别
方法 | 作用 |
---|---|
notify() | 唤醒在该对象的监视器上等待的单个线程。具体唤醒哪个线程由 JVM 决定。 |
notifyAll() | 唤醒在该对象的监视器上等待的所有线程。所有线程会竞争锁,继续执行。 |
总结
notify()
是线程间通信的重要方法,通常与wait()
配合使用。- 它用于唤醒等待的线程,但只能唤醒一个线程。
- 在多线程编程中,
notify()
和wait()
的使用需要谨慎,确保线程安全和避免死锁。 - 如果需要唤醒所有线程,可以使用
notifyAll()
。
五、notifyAll()方法
notifyAll()
是 Java 中用于线程间通信的重要方法,属于 Object
类的一部分。它通常与 wait()
方法一起使用,用于在多线程环境中实现线程的协调和同步。
1. notifyAll()
的作用
notifyAll()
用于唤醒所有正在等待(调用 wait()
方法)的线程。与 notify()
不同,notify()
只会随机唤醒一个等待线程,而 notifyAll()
会唤醒所有等待线程。
- 适用场景:当多个线程需要被唤醒并重新竞争锁时,使用
notifyAll()
。 - 调用前提:必须在同步代码块(
synchronized
块)或同步方法中调用,否则会抛出IllegalMonitorStateException
。
2. notifyAll()
的使用方法
notifyAll()
的调用方式如下:
synchronized (锁对象) {
锁对象.notifyAll();
}
示例代码
class SharedResource {
private boolean flag = false;
public synchronized void waitForFlag() throws InterruptedException {
while (!flag) {
wait(); // 当前线程进入等待状态
}
System.out.println(Thread.currentThread().getName() + " 被唤醒,继续执行");
}
public synchronized void setFlag() {
flag = true;
notifyAll(); // 唤醒所有等待的线程
}
}
public class NotifyAllExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
// 创建多个等待线程
Runnable waitTask = () -> {
try {
resource.waitForFlag();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread t1 = new Thread(waitTask, "线程1");
Thread t2 = new Thread(waitTask, "线程2");
Thread t3 = new Thread(waitTask, "线程3");
Thread t4 = new Thread(waitTask, "线程4");
Thread t5 = new Thread(waitTask, "线程5");
Thread t6 = new Thread(waitTask, "线程6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
// 主线程休眠一段时间后唤醒所有等待线程
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.setFlag(); // 唤醒所有等待的线程
}
}
输出结果
线程6 被唤醒,继续执行
线程3 被唤醒,继续执行
线程4 被唤醒,继续执行
线程5 被唤醒,继续执行
线程2 被唤醒,继续执行
线程1 被唤醒,继续执行
随机唤醒一个线程
3. notifyAll()
的工作原理
- 唤醒所有等待线程:
notifyAll()
会唤醒所有在锁对象上调用wait()
的线程。 - 重新竞争锁:被唤醒的线程会从
wait()
方法返回,并尝试重新获取锁。只有一个线程能成功获取锁,其他线程会继续等待。 - 条件检查:通常需要在
wait()
返回后再次检查条件(如while (!flag)
),因为多个线程可能被唤醒,但条件可能只对其中一个线程成立。
4. notifyAll()
与 notify()
的区别
特性 | notify() | notifyAll() |
---|---|---|
唤醒线程数量 | 随机唤醒一个等待线程 | 唤醒所有等待线程 |
适用场景 | 只有一个线程需要被唤醒时使用 | 多个线程需要被唤醒时使用 |
锁竞争 | 只有一个线程会竞争锁 | 所有被唤醒线程会竞争锁 |
5. 注意事项
- 必须在同步块中调用:
notifyAll()
和wait()
必须在synchronized
块或方法中调用,否则会抛出IllegalMonitorStateException
。 - 条件检查:被唤醒的线程应重新检查条件,因为多个线程可能被唤醒,但条件可能只对其中一个线程成立。
- 避免虚假唤醒:即使没有调用
notify()
或notifyAll()
,线程也可能从wait()
中返回(称为虚假唤醒)。因此,通常将wait()
放在while
循环中。
6. 总结
notifyAll()
用于唤醒所有等待的线程,适用于多线程需要同时被唤醒的场景。- 与
wait()
配合使用,可以实现线程间的协调和同步。 - 使用时需注意同步锁、条件检查和虚假唤醒问题。
六、start()方法
start()
是 Java 中 Thread
类的一个重要方法,用于启动一个线程。调用 start()
方法后,线程会进入就绪状态,等待 CPU 调度执行。以下是关于 start()
方法的详细说明:
1. start()
方法的作用
- 启动线程:
start()
方法会启动一个新的线程,并执行run()
方法中的代码。 - 线程生命周期:调用
start()
后,线程从 新建状态(New) 进入 就绪状态(Runnable),等待 CPU 调度执行。 - 异步执行:
start()
方法会立即返回,不会等待run()
方法执行完毕,因此线程是异步执行的。
2. start()
方法的使用
语法
Thread thread = new Thread(任务);
thread.start();
示例代码
public class StartExample {
public static void main(String[] args) {
// 创建一个线程任务
Runnable task = () -> {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
};
// 创建线程对象
Thread thread = new Thread(task, "MyThread");
// 启动线程
thread.start();
// 主线程继续执行
System.out.println("主线程 " + Thread.currentThread().getName() + " 继续执行");
}
}
输出结果
主线程 main 继续执行
线程 MyThread 正在运行
3. start()
方法的特点
- 只能调用一次:
- 每个线程的
start()
方法只能调用一次,重复调用会抛出IllegalThreadStateException
。 - 示例:
- 每个线程的
Thread thread = new Thread(() -> System.out.println("线程运行"));
thread.start();
thread.start(); // 抛出 IllegalThreadStateException
- 启动新线程:
start()
方法会启动一个新的线程,而不是在当前线程中直接调用run()
方法。- 如果直接调用
run()
,代码会在当前线程中同步执行,不会启动新线程。
- 线程调度:
- 调用
start()
后,线程进入就绪状态,具体何时执行由操作系统调度决定。
- 调用
4. start()
与 run()
的区别
特性 | start() | run() |
---|---|---|
线程启动 | 启动新线程,异步执行 run() 方法 | 直接在当前线程中同步执行 run() 方法 |
调用次数 | 每个线程只能调用一次 | 可以多次调用 |
线程生命周期 | 线程从新建状态进入就绪状态 | 不涉及线程状态变化 |
示例对比
Thread thread = new Thread(() -> System.out.println("线程运行"));
thread.start(); // 启动新线程,异步执行
thread.run(); // 直接在当前线程中同步执行
5. 注意事项
- 线程安全问题:
- 如果多个线程共享资源,需要注意线程安全问题,使用同步机制(如
synchronized
或Lock
)来保护共享资源。
- 如果多个线程共享资源,需要注意线程安全问题,使用同步机制(如
- 线程池:
- 在实际开发中,推荐使用线程池(如
ExecutorService
)来管理线程,而不是直接创建和启动线程。
- 在实际开发中,推荐使用线程池(如
- 线程命名:
- 可以通过
Thread
的构造方法或setName()
方法为线程设置名称,便于调试和日志记录。
- 可以通过
6. 总结
start()
方法用于启动线程,使线程进入就绪状态,异步执行run()
方法。- 每个线程的
start()
方法只能调用一次。 - 与
run()
不同,start()
会启动新线程,而不是在当前线程中同步执行。
七、run()方法
run()
是 Java 中 Thread
类的一个核心方法,用于定义线程执行的任务。它是 Runnable
接口的唯一方法,也是线程实际执行的代码逻辑所在。以下是关于 run()
方法的详细说明:
1. run()
方法的作用
- 定义线程任务:
run()
方法包含线程需要执行的代码逻辑。 - 线程执行入口:当线程启动后,JVM 会自动调用
run()
方法。 - 同步执行:如果直接调用
run()
方法,代码会在当前线程中同步执行,而不会启动新线程。
2. run()
方法的使用
语法
public void run() {
// 线程执行的代码逻辑
}
示例代码
public class RunExample {
public static void main(String[] args) {
// 创建一个线程任务
Runnable task = () -> {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
};
// 创建线程对象
Thread thread = new Thread(task, "MyThread");
// 直接调用 run() 方法
thread.run(); // 注意:这里不会启动新线程
// 主线程继续执行
System.out.println("主线程 " + Thread.currentThread().getName() + " 继续执行");
}
}
输出结果
线程 main 正在运行
主线程 main 继续执行
3. run()
方法的特点
- 线程任务定义:
run()
方法是线程执行的核心逻辑,通常通过实现Runnable
接口或继承Thread
类来重写run()
方法。
- 直接调用是同步的:
- 如果直接调用
run()
方法,代码会在当前线程中同步执行,不会启动新线程。 - 示例:
- 如果直接调用
Thread thread = new Thread(() -> System.out.println("线程运行"));
thread.run(); // 直接调用 run(),不会启动新线程
- 与
start()
的区别:start()
方法会启动新线程并异步执行run()
方法。run()
方法只是普通的方法调用,不会启动新线程。
4. 实现 run()
的两种方式
方式 1:实现 Runnable
接口
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread thread = new Thread(new MyTask(), "MyThread");
thread.start(); // 启动新线程
}
}
方式 2:继承 Thread
类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动新线程
}
}
5. run()
与 start()
的区别
特性 | run() | start() |
---|---|---|
线程启动 | 不会启动新线程,代码在当前线程中同步执行 | 启动新线程,异步执行 run() 方法 |
调用方式 | 直接调用 | 通过线程对象调用 |
线程生命周期 | 不涉及线程状态变化 | 线程从新建状态进入就绪状态 |
示例对比
Thread thread = new Thread(() -> System.out.println("线程运行"));
thread.run(); // 直接调用 run(),同步执行
thread.start(); // 调用 start(),启动新线程异步执行
6. 注意事项
- **不要直接调用 **
run()
:- 如果需要启动新线程,应该调用
start()
方法,而不是直接调用run()
。
- 如果需要启动新线程,应该调用
- 线程安全问题:
- 如果多个线程共享资源,需要在
run()
方法中实现线程同步机制(如synchronized
或Lock
)。
- 如果多个线程共享资源,需要在
- 线程池:
- 在实际开发中,推荐使用线程池(如
ExecutorService
)来管理线程,而不是直接创建和启动线程。
- 在实际开发中,推荐使用线程池(如
7. 总结
run()
方法定义了线程执行的任务逻辑。- 直接调用
run()
方法会在当前线程中同步执行,不会启动新线程。 - 启动新线程需要通过
start()
方法,JVM 会自动调用run()
方法。
八、isAlive()方法
isAlive()
是 Java 中 Thread
类的一个方法,用于检查线程是否仍然处于活动状态。线程的“活动状态”指的是线程已经启动(调用了 start()
方法)但尚未终止(run()
方法未执行完毕)的状态。
1. isAlive()
方法的作用
- 检查线程状态:
isAlive()
方法用于判断线程是否仍然处于活动状态。 - 返回值:
true
:线程已经启动且尚未终止。false
:线程尚未启动或已经终止。
2. isAlive()
方法的使用
语法
boolean isAlive = thread.isAlive();
示例代码
public class IsAliveExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个线程任务
Runnable task = () -> {
try {
Thread.sleep(1000); // 模拟线程执行任务
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 创建线程对象
Thread thread = new Thread(task, "MyThread");
// 检查线程是否活动
System.out.println("线程启动前是否活动: " + thread.isAlive()); // false
// 启动线程
thread.start();
System.out.println("线程启动后是否活动: " + thread.isAlive()); // true
// 主线程等待子线程执行完毕
thread.join();
System.out.println("线程执行完毕后是否活动: " + thread.isAlive()); // false
}
}
输出结果
线程启动前是否活动: false
线程启动后是否活动: true
线程执行完毕后是否活动: false
3. isAlive()
方法的特点
- 线程状态判断:
- 线程在以下情况下会返回
true
:- 线程已经启动(调用了
start()
方法)。 - 线程的
run()
方法尚未执行完毕。
- 线程已经启动(调用了
- 线程在以下情况下会返回
false
:- 线程尚未启动。
- 线程的
run()
方法已经执行完毕。
- 线程在以下情况下会返回
- 与线程生命周期的关系:
- 线程的生命周期包括:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked) 和 终止(Terminated)。
isAlive()
方法在 就绪、运行 和 阻塞 状态下返回true
,在 新建 和 终止 状态下返回false
。
- 适用场景:
- 用于监控线程的执行状态。
- 在多线程协作中,判断某个线程是否已经完成任务。
4. 注意事项
- 线程启动前:
- 在调用
start()
方法之前,isAlive()
会返回false
。
- 在调用
- 线程终止后:
- 当线程的
run()
方法执行完毕后,isAlive()
会返回false
。
- 当线程的
- 线程阻塞:
- 如果线程处于阻塞状态(如调用了
sleep()
、wait()
或join()
),isAlive()
仍然返回true
。
- 如果线程处于阻塞状态(如调用了
- 线程中断:
- 如果线程被中断(调用
interrupt()
),但run()
方法尚未执行完毕,isAlive()
仍然返回true
。
- 如果线程被中断(调用
5. 示例:监控线程状态
public class MonitorThreadExample {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000); // 模拟线程执行任务
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
// 监控线程状态
while (thread.isAlive()) {
System.out.println("线程仍在运行...");
Thread.sleep(500); // 每隔 500ms 检查一次
}
System.out.println("线程已终止");
}
}
输出结果
线程仍在运行...
线程仍在运行...
线程仍在运行...
线程仍在运行...
线程已终止
6. 总结
isAlive()
方法用于检查线程是否处于活动状态。- 返回
true
表示线程已经启动且尚未终止,返回false
表示线程尚未启动或已经终止。 - 适用于监控线程状态或判断线程是否完成任务。