Java多线程基础 Thread类 常用方法
Java线程常用方法
方法名 | static | 说明 | 注意 |
---|---|---|---|
start() | 启动一个新线 程,在新的线程 运行 run 方法 中的代码 | start 方法只是让线程进入就绪,里面代码不一定立刻 运行(CPU 的时间片还没分给它)。每个线程对象的 start方法只能调用一次,如果调用了多次会出现 IllegalThreadStateException | |
run() | 新线程启动后会 调用的方法 | 如果在构造 Thread 对象时传递了 Runnable 参数,则 线程启动后会调用 Runnable 中的 run 方法,否则默 认不执行任何操作。但可以创建 Thread 的子类对象, 来覆盖默认行为 | |
join() | 等待线程运行结 束 | ||
join(long n) | 等待线程运行结 束,最多等待 n 毫秒 | ||
getId() | 获取线程长整型 的 id | id 唯一 | |
getName() | 获取线程名 | ||
setName(String) | 修改线程名 | ||
getPriority() | 获取线程优先级 | ||
setPriority(int) | 修改线程优先级 | java中规定线程优先级是1~10 的整数,较大的优先级 能提高该线程被 CPU 调度的机率 | |
getState() | 获取线程状态 | Java 中线程状态是用 6 个 enum 表示,分别为: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED | |
isInterrupted() | 判断是否被打断 | 不会清除 打断标记 | |
isAlive() | 线程是否存活 (还没有运行完 毕) | ||
interrupt() | 打断线程 | 如果被打断线程正在 sleep,wait,join 会导致被打断 的线程抛出 InterruptedException,并清除 打断标记 ;如果打断的正在运行的线程,则会设置 打断标记 ;park 的线程被打断,也会设置 打断标记 | |
interrupted() | static | 判断当前线程是 否被打断 | 会清除 打断标记 |
currentThread() | static | 获取当前正在执 行的线程 | |
sleep(long n) | static | 让当前执行的线 程休眠n毫秒, 休眠时让出 cpu 的时间片给其它 线程 | |
yield() | static | 提示线程调度器 让出当前线程对 CPU的使用 | 主要是为了测试和调试 |
start 与 run
直接调用 run 是在主线程中执行了 run,没有启动新的线程
使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码
Thread t1 = new Thread("t1"){
@Override
public void run() {
log.info(Thread.currentThread().getName());
}
};
t1.run();
log.info("main end");
输出
18:20:15 [main] c - main
18:20:15 [main] c - main end
程序仍在 main 线程运行
将 run 改为 start
Thread t1 = new Thread("t1"){
@Override
public void run() {
log.info(Thread.currentThread().getName());
}
};
t1.start();
log.info("main end");
输出
18:21:42 [main] c - main end
18:21:42 [t1] c - t1
程序在 t1 线程运行
start 只能调用一次 重复调用会抛出 IllegalThreadStateException
从start源码可以看到 线程状态 != 0 也就是新建状态 就会抛出 IllegalThreadStateException
sleep 与 yield
sleep:
- 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
- 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
- 睡眠结束后的线程未必会立刻得到执行
- 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
yield:
- 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
- 具体的实现依赖于操作系统的任务调度器
Runnable task1 = () -> {
while (true) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
Runnable task2 = () -> {
while (true) {
Thread.yield();
}
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
t2.start();
t1.start();
System.out.println("t2 线程状态" + t2.getState());
System.out.println("t1 线程状态" + t1.getState());
可以看到线程调用 sleep
会状态转为阻塞
,调用yield
后转为就绪。
差别就是 Thread.yield()
会让出CPU资源,但不进入休眠状态,而是等待线程调度器重新选择它。,Thread.sleep()
会进入休眠状态,不占用CPU资源,而在指定时间后自动唤醒。
.
join 方法详解
join()
是 Java 多线程编程中一个非常重要的方法,它允许一个线程等待另一个线程完成后再继续执行。下面是对 join()
方法的详细解释:
-
功能:
join()
方法用于让一个线程等待另一个线程完成。当一个线程调用另一个线程的join()
方法时,它将暂停自己的执行,直到被等待的线程执行完毕。这通常用于协调线程的执行顺序,确保在某个线程完成后再执行另一个线程。 -
异常:
join()
方法声明了可能抛出InterruptedException
异常,这是因为线程可以被中断。如果等待的线程在等待时被中断,join()
方法会抛出InterruptedException
异常。 -
重载方法:
join()
还有一个重载方法,可以指定等待的时间,如下所示:public final synchronized void join(long millis) throws InterruptedException public final synchronized void join(long millis, int nanos) throws InterruptedException
这些重载方法允许你等待一段指定的时间,如果等待的时间超过了指定时间,线程将会继续执行。
-
使用示例:
下面是一个简单的示例,演示如何使用join()
方法:public static void main(String[] args) { Thread thread1 = new Thread(() -> { for (int i = 1; i <= 5; i++) { Thread.sleep(10); System.out.println("Thread 1: " + i); } }); Thread thread2 = new Thread(() -> { for (int i = 1; i <= 5; i++) { System.out.println("Thread 2: " + i); } }); thread1.start(); try { thread1.join(); // 主线程等待 thread1 执行完 } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); }
输出
Thread 1: 1 Thread 1: 2 Thread 1: 3 Thread 1: 4 Thread 1: 5 Thread 2: 1 Thread 2: 2 Thread 2: 3 Thread 2: 4 Thread 2: 5
在上述示例中,主线程启动了
thread1
,然后调用thread1.join()
,这会使主线程等待thread1
执行完毕。然后,主线程再启动thread2
。
修改 thread1.join() -> thread1.join(25)
输出
主线程启动了 thread1
,然后调用 thread1.join(25)
,这会使主线程等待 thread1
执行25ms。然后,主线程启动 thread2
。
Thread 1: 1
Thread 1: 2
Thread 2: 1
Thread 2: 2
Thread 2: 3
Thread 2: 4
Thread 2: 5
Thread 1: 3
Thread 1: 4
Thread 1: 5
interrupt 方法详解
interrupt()
方法是 Java 多线程编程中用于中断线程的方法。它允许一个线程通知另一个线程,请求它终止执行或进行一些其他处理。下面是 interrupt()
方法的详细解释:
-
功能:
interrupt()
方法用于中断一个线程,即通知目标线程中断它的执行。线程可以通过检查其中断状态来响应中断请求,并根据需要采取适当的措施。中断并不直接停止线程的执行,它只是发送一个中断请求。 -
中断状态:
- 每个线程都有一个中断状态,它是一个布尔值,默认为
false
。 - 调用
interrupt()
方法会将目标线程的中断状态设置为true
。 - 可以使用
Thread.isInterrupted()
方法来检查线程的中断状态,或者使用Thread.interrupted()
静态方法来检查当前线程的中断状态。
- 每个线程都有一个中断状态,它是一个布尔值,默认为
-
响应中断:
- 当一个线程被中断时,它可以根据需要采取不同的行动,如终止线程、抛出
InterruptedException
异常或继续执行。响应中断的方式取决于线程的设计和需求。 - 典型的响应中断方式包括检查中断状态并做相应处理,如退出循环或释放资源。
- 当一个线程被中断时,它可以根据需要采取不同的行动,如终止线程、抛出
-
InterruptedException 异常:
- 在某些情况下,线程可能会调用会抛出
InterruptedException
异常的方法,如Object.wait()
,Thread.sleep()
, 和BlockingQueue.take()
。 - 当一个线程被中断时,这些方法可能抛出
InterruptedException
异常,通常用于指示线程应该终止执行。
- 在某些情况下,线程可能会调用会抛出
-
使用示例:
public class InterruptExample { public static void main(String[] args) { Thread worker = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { System.out.println("Working..."); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Thread interrupted while sleeping."); // 清除中断状态 Thread.currentThread().interrupt(); // 重新设置中断状态 } } System.out.println("Worker thread exiting."); }); worker.start(); // 主线程等待一段时间后中断 worker 线程 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } worker.interrupt(); // 中断 worker 线程 } } // 输出 Working... Working... Working... Working... Working... Thread interrupted while sleeping. Worker thread exiting.
在上述示例中,
worker
线程在一个循环中执行工作,它会检查自己的中断状态,如果被中断,则退出循环。主线程在启动worker
线程后等待一段时间,然后调用interrupt()
方法来中断worker
线程的执行。
interrupt
打断阻塞状态
的线程和正常运行
的线程区别:
-
阻塞状态的线程:
当一个线程处于阻塞状态(如调用
Object.wait()
,Thread.sleep()
,BlockingQueue.take()
等方法时)并且被调用了interrupt()
方法,会出现以下情况:- 线程会抛出
InterruptedException
异常:这是Java标准库的多个阻塞方法的一种通用行为,以通知线程被中断。 - 线程的中断状态被清除:
interrupted()
方法返回true
,isInterrupted()
方法返回true
,但是线程的中断状态会被重置为false
。
阻塞状态的线程通常会捕获
InterruptedException
异常,然后根据需要采取适当的处理。这可以包括释放资源、终止线程或继续执行,具体取决于线程的设计和需求。 - 线程会抛出
-
正常运行的线程:
当一个线程处于正常运行状态(没有被阻塞),并且被调用了
interrupt()
方法,会出现以下情况:- 线程不会立即停止:
interrupt()
方法仅仅是将线程的中断状态设置为true
。 - 线程需要显式检查中断状态:线程在适当的地方需要主动检查自身的中断状态,然后根据需要决定是否终止执行。
正常运行的线程需要在代码中显式检查中断状态,通常使用
Thread.isInterrupted()
方法或Thread.interrupted()
方法(后者会清除中断状态)来检查线程的中断状态,并在适当的时候决定终止执行。这可以是通过抛出InterruptedException
来终止线程,或者通过采取其他终止操作。 - 线程不会立即停止:
打断正常运行的线程 如果不检测自身的中断状态是没效果的
Thread t1 = new Thread
for (int i = 0; i
System.out.pri
}
});
t1.start();
t1.interrupt();
// 输出
0
1
2
3
4
使用interrupted
判断 会清除打断标记
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
// 会清除打断标记
if (!Thread.interrupted()){
System.out.println(i);
}else {
System.out.println("中断处理");
}
}
});
t1.start();
t1.interrupt();
// 输出
中断处理 // 因为中断标记被清除 所以中断处理只会有一次
1
2
3
4
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
// 不会清除打断标记
if (!Thread.currentThread().isInterrupted()){
System.out.println(i);
}else {
System.out.println("中断处理");
}
}
});
t1.start();
t1.interrupt();
// 输出
中断处理
中断处理
中断处理
中断处理
中断处理
不推荐使用的方法
方法名 | static | 作用 | 说明 |
---|---|---|---|
stop() | 停止线程运行 | 这个方法被弃用,因为它会强制终止线程,可能导致资源泄漏和不一致的程序状态。取而代之,应该使用更安全的方式来协调线程的停止,例如通过设置一个标志,使线程自行停止。 | |
suspend() | 挂起(暂停)线程运行 | Thread.suspend() 和 Thread.resume() 方法:这两个方法也已被弃用,因为它们可能导致线程死锁或其他问题。建议使用更安全的线程挂起和恢复机制,例如 wait() 和 notify() 或使用高级并发库中的工具。 | |
resume() | 恢复线程运行 |