目录
正文:
1.线程创建
2.线程休眠
3.获取线程实例
4.线程中断
5.线程等待join()
总结:
正文:
1.线程创建
线程创建是多线程编程的第一步,它涉及到创建一个可以并行执行的新线程。在Java中,有几种不同的方法可以创建线程。以下是对线程创建的详细分析:
1. 继承 Thread
类
创建一个继承自 Thread
类的新类,并重写 run()
方法来定义线程执行的任务。
public class MyThread extends Thread {@Overridepublic void run() {// 线程执行的代码System.out.println("继承Thread实现方式");}
}public class Main {public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程}
}
在这个例子中,MyThread
类继承了 Thread
类,并重写了 run()
方法。创建 MyThread
的实例并通过调用 start()
方法来启动新线程。
2. 实现 Runnable
接口
创建一个实现 Runnable
接口的新类,并实现 run()
方法。然后,将这个类的实例传递给 Thread
类的构造函数。
public class MyRunnable implements Runnable {@Overridepublic void run() {// 线程执行的代码System.out.println("Runnable实现方式");}
}public class Main {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start(); // 启动线程}
}
3. 使用匿名内部类
创建一个匿名内部类,同时继承 Thread
类或实现 Runnable
接口,并在创建时定义线程任务。
public class test {public static void main(String[] args) {new Thread(() -> {// 线程执行的代码System.out.println("使用匿名类创建 Thread 子类对象");}).start(); // 启动线程// 使用匿名类创建 Runnable 子类对象Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("使用匿名类创建 Runnable 子类对象");}});}
}
匿名内部类还可以简化成lambda表达式:
// 使用 lambda 表达式创建 Runnable 子类对象
Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象"));
Thread t4 = new Thread(() -> {
System.out.println("使用匿名类创建 Thread 子类对象");
});
优缺点:
- 使用实现Runnable接口的方式可以更好地符合面向对象设计原则,因为Java是单继承的语言,如果继承了Thread类就无法再继承其他类。实现Runnable接口的方式更灵活,可以将线程的任务与线程本身分离,使代码结构更清晰。但是需要额外创建一个类或匿名内部类来实现Runnable接口,这可能增加了代码的复杂性。
- 继承Thread,简单直接,易于理解和实现。允许重写
run()
方法以及其他Thread
类的方法,如start()
、interrupt()
等。但是由于Java不支持多重继承,一个类只能继承自Thread或实现Runnable接口,这限制了类的复用性。 继承Thread类意味着不能使用原有类的名字,可能导致类名的混淆。 - 使用匿名内部类可以减少代码量,不需要单独定义一个类。适合那些只需要执行一次任务的简单场景。但是可读性较差,特别是当匿名内部类的代码较长时。调试困难,因为匿名内部类没有明确的名字。不利于代码的复用和维护。
2.线程休眠
线程休眠是指让当前执行的线程暂停执行一段时间。在这段时间内,线程不会执行任何操作,但也不会释放任何锁资源。
工作原理:
- 调用
sleep()
:当当前线程调用Thread.sleep()
方法时,它会暂停执行,并进入休眠状态。 - 等待指定时间:线程在休眠状态下会等待指定的时间。在这段时间内,线程不会执行任何代码,也不会消耗CPU资源。
- 自动唤醒:休眠时间结束后,线程会被自动唤醒,并继续执行
sleep()
方法之后的代码。 - 响应中断:如果线程在休眠期间被中断(例如,通过调用
Thread.interrupt()
方法),它会抛出InterruptedException
异常。
Thread.sleep()
方法有以下几个重载版本:
sleep(long millis)
:使当前线程休眠指定的毫秒数。sleep(long millis, int nanos)
:使当前线程休眠指定的毫秒数加纳秒数。sleep(long millis, int nanos, long offset)
(Java 11新增):使当前线程休眠指定的毫秒数加纳秒数,并且可以通过offset
参数来调整系统时间。
示例代码:
public class SleepDemo {public static void main(String[] args) {System.out.println("Thread is going to sleep for 3 seconds.");try {Thread.sleep(3000); // 休眠3秒} catch (InterruptedException e) {System.out.println("Thread was interrupted.");// 可以选择在这里处理中断,例如记录日志或清理资源}System.out.println("Thread has woken up.");}
}
3.获取线程实例
获取线程实例是Java多线程编程中的一个基本操作,它允许程序获取到当前正在执行代码的线程对象。
工作原理:
- 调用
currentThread()
:在任何线程的执行流程中,调用Thread.currentThread()
将返回当前线程的实例。 - 获取线程信息:返回的
Thread
对象包含了线程的详细信息,如名称、ID、状态等。 - 使用线程信息:通过这个返回的线程实例,可以执行其他操作,如检查线程状态、获取线程名称或管理线程(如果有必要)。
示例代码:
public class CurrentThreadInfo {public static void main(String[] args) {Thread currentThread = Thread.currentThread();System.out.println("Current thread name: " + currentThread.getName());System.out.println("Current thread ID: " + currentThread.getId());}
}
在这个示例中,我们获取了当前线程的名称和ID,并打印到控制台。
运行结果:
4.线程中断
线程中断是一种线程间的通信机制,可以用来请求线程停止正在执行的任务。它提供了一种协作机制来优雅地停止线程的执行。当一个线程被中断时,它会收到一个中断信号,并且interrupt()
方法会被调用。线程可以响应中断信号,并根据设计者的选择来处理中断。
1.interrupt()方法
interrupt()
方法是一种请求中断线程的机制。它不会立即停止线程的执行,而是设置线程的中断状态,使线程能够根据这个状态来决定自己的行为。interrupt()
方法是 Thread
类的一个实例方法,可以被任何其他线程调用,用来请求中断目标线程。
interrupt()
方法的工作原理
-
设置中断状态:当
interrupt()
方法被调用时,它会将目标线程的中断状态设置为true
。 -
线程检查中断状态:线程需要定期检查自己的中断状态。这通常在执行关键操作之后或者在循环的检查点进行。
-
响应中断:线程可以选择响应中断状态,执行必要的清理工作,并安全地退出。线程也可以选择忽略中断状态,继续执行。
示例代码:
class InterruptibleTask implements Runnable {@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {try {// 执行任务Thread.sleep(1000); // 休眠1秒System.out.println("执行任务");} catch (InterruptedException e) {// 处理中断System.out.println("程序已经被中断了");// 清理资源return; // 退出run方法}}}
}public class test {public static void main(String[] args) {Thread taskThread = new Thread(new InterruptibleTask());taskThread.start();// 让任务线程运行一段时间try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 请求中断任务线程taskThread.interrupt();System.out.println("请求中断");}
}
在这个例子中,InterruptibleTask
在一个循环中执行任务,并在每次迭代后检查是否被中断。当主线程调用 taskThread.interrupt()
时,任务线程会捕获 InterruptedException
并安全地退出。
运行结果:
2.interrupted()方法
interrupted()
是 Java 中 Thread
类的一个静态方法,它用于检查当前线程的中断状态,并且与 isInterrupted()
方法不同,interrupted()
在检查后会清除中断状态。这个方法通常用于流式操作中,以便在处理中断时能够保持中断状态的一致性。
interrupted()
方法的工作原理
- 检查中断状态:
interrupted()
方法检查当前线程的中断状态。 - 清除中断状态:无论中断状态如何,
interrupted()
方法都会将中断状态重置为未中断(false)。 - 返回中断状态:该方法返回当前线程的中断状态。
示例代码:
public class test {public static void main(String[] args) {Thread.currentThread().interrupt(); // 设置当前线程的中断状态while (true) {System.out.println("执行interrupted之前,此时线程的中断状态是:" + Thread.currentThread().isInterrupted());if (Thread.interrupted()) {System.out.println("执行interrupted之后,此时线程的中断状态是:" + Thread.currentThread().isInterrupted());}else{break;}}System.out.println("进程结束");}
}
在这个例子中,我们首先通过调用 Thread.currentThread().interrupt()
方法来设置当前线程的中断状态。然后在一个循环中,我们使用 Thread.interrupted()
方法来检查中断状态。由于 interrupted()
方法会清除中断状态,所以循环中的其他操作不会受到中断的影响。一旦检测到中断,我们执行清理操作并退出循环。
运行结果为:
3.isInterrupted()方法
在Java中,isInterrupted()方法是用来检查线程的中断状态,但不会清除线程的中断状态。这个方法是一个实例方法,可以通过Thread类的实例来调用。
isInterrupted()
方法的工作原理
- 查询中断状态:
isInterrupted()
方法检查调用它的线程的中断状态。 - 返回中断状态:该方法返回一个布尔值,表示线程的中断状态。如果线程被中断过,或者中断状态被设置过,它将返回
true
。 - 中断状态不清除:调用
isInterrupted()
方法后,线程的中断状态保持不变。
示例代码:
public class test {public static void main(String[] args) {Thread.currentThread().interrupt(); // 设置当前线程的中断状态while (true) {System.out.println("执行isInterrupted之前,此时线程的中断状态是:" + Thread.currentThread().isInterrupted());if (Thread.currentThread().isInterrupted()) {System.out.println("执行isInterrupted之后,此时线程的中断状态是:" + Thread.currentThread().isInterrupted());}else{break;}}System.out.println("进程结束");}
}
还是这个代码,但是条件变成了isInterrupted,这个方法并不会清除中断状态 ,所以会导致程序死循环。
截取一段运行结果:
5.线程等待join()
在Java中,线程等待(join)是一种线程同步的机制,允许一个线程等待另一个线程的结束。这是通过Thread
类的join()
方法实现的。当一个线程A在另一个线程B上调用join()
时,线程A会阻塞,直到线程B完成执行。
join()
方法的工作原理
- 阻塞等待:当线程A调用线程B的
join()
方法时,线程A会暂停执行,直到线程B的run()
方法执行完毕。 - 释放锁:在
join()
方法调用期间,线程A会释放它持有的任何同步锁,这允许其他线程在这些锁上操作。 - 继续执行:一旦线程B完成执行,线程A会解除阻塞,并继续执行
join()
方法之后的代码。
join()
方法的重载版本
- 无参数的
join()
:调用线程会无限期等待,直到被调用join()
的线程终止。 - 带超时参数的
join(long millis)
:调用线程会等待指定的时间(毫秒)。如果被调用的线程在这段时间内没有终止,调用线程将解除阻塞并继续执行。
示例代码:
public class test {public static void main(String[] args) {//获取main线程的对象Thread mainthread = Thread.currentThread();Thread threadB = new Thread(() -> {System.out.println("线程B正在执行中");System.out.println("线程B执行时主线程的状态为:" + mainthread.getState());// 模拟长时间任务try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程B执行结束");});threadB.start();try {threadB.join(); // 主线程等待线程B完成System.out.println("B已经完成,主线程的状态为:" + mainthread.getState());} catch (InterruptedException e) {e.printStackTrace();}}
}
在这个例子中,主线程创建并启动了线程B,然后调用threadB.join()
等待线程B完成。主线程会阻塞,直到线程B的run()
方法执行完毕。
运行结果为:
总结:
Thread类是Java中实现多线程编程的重要类之一,通过Thread类可以创建、启动和控制线程的执行。在多线程编程中,合理使用Thread类可以实现并发执行、资源共享和协作等功能。