你好,我是
一位朋友在面试中被问到:如何判断线程是否已经启动?
不想一开始就给出答案,而是逐步分析问题,掌握好知识的来龙去脉,不要把知识学的那么枯燥无味,不然你迟早要被劝退。
我们快速简单地了解一下线程与进程。
定义 :进程是程序的一次执行过程,是系统资源分配的基本单位,每个进程之间相互独立。
特点 :
生活案例 :想象你在一台桌面电脑上打开了多个软件程序,比如同时运行着一个浏览器、一个文字处理软件以及一个视频播放器,每个软件就是一个独立的进程。每个软件有自己的内存空间和资源,彼此之间独立运行。
定义 :线程是进程内的执行单元,是 CPU 调度的基本单位,多个线程共享进程的资源。
特点 :
生活案例 :想象你在早晨做早饭的时候,同时负责煎蛋、煮咖啡和煮面,这就好比在一个进程中有多个线程在并行执行不同的任务。每个任务可以看作是一个线程,它们共享厨房这个整体资源。
不要一味地学技术以及背面试题,我们要想办法让技术回归生活案例中。
估计你也看过很多文章讲线程的创建方式有3种,4种,5种,6种,7种....。
不是说的种类越多显得你越牛X,而是要知道每一种的特点以及使用场景那才是真材实料。
我这里说三种,不想讲太多,没有多大意义。
1. 继承 Thread 类方式 :通过创建类并继承 Thread 类,重写 run() 方法来定义线程的执行逻辑。
// 继承 Thread 类创建线程
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
// 创建并启动线程
MyThread thread = new MyThread();
thread.start();
}
}
这里有个很大的问题,那就是Java只支持单继承,为了解决单继承的问题咱们可以使用实现Runnable接口来创建线程。
2. 实现 Runnable 接口方式 :通过创建类实现 Runnable 接口,实现 run() 方法来定义线程的执行逻辑。然后将实现了 Runnable 接口的对象传递给 Thread 类的构造方法。
// 实现 Runnable 接口创建线程
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable running");
}
}
public class Main {
public static void main(String[] args) {
// 创建线程并传入实现了 Runnable 接口的对象
Thread thread = new Thread(new MyRunnable());
thread.start();
}
}
单继承的问题解决了,可是他们两都有个共同的问题:没有返回值 。
为此,我们可以使用实现Callable 接口来解决。
3.实现Callable接口 :使用 Callable 接口配合 Future 和 ExecutorService 来创建线程并返回结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
// 实现 Callable 接口创建线程
class MyCallable implements Callable<String> {
@Override
public String call() {
return "Callable thread is running";
}
}
public class Main {
public static void main(String[] args) {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(1);
// 提交 Callable 任务并获取 Future 对象
Future<String> future = executor.submit(new MyCallable());
try {
// 获取线程执行结果
String result = future.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
// 关闭线程池
executor.shutdown();
}
}
通过实现 Callable 接口并重写 call() 方法来定义线程的执行逻辑。然后使用 ExecutorService 创建线程池并提交 Callable 任务,得到一个 Future 对象,通过该对象可以获取线程执行结束后的返回结果。
都是一环扣一环的,先是什么问题,解决了什么问题后,还是存在什么问题,又是怎么解决的。有点类似于我们做项目开发,很多时候我们的项目需要重构、拆分以及合并,都是为了解决某些问题。
在我们Java语言中,线程的状态主要由 Thread.State
枚举类定义。这些状态描述了线程在其生命周期中可能经历的不同阶段。以下是 Java 中线程的几种状态:
NEW(新建) :当线程对象被创建但还未启动时,线程处于 NEW 状态。
RUNNABLE(可运行) :线程在操作系统中处于可运行状态,等待被调度执行。
BLOCKED(阻塞) :线程因为等待监视器锁(synchronized 关键字)而被阻塞。
WAITING(等待) :线程在等待特定条件触发的情况下进入等待状态,例如调用 Object.wait() 方法。
TIMED_WAITING(计时等待) :线程在有超时限制的情况下等待,例如调用带有超时参数的 Object.wait(timeout) 或 Thread.sleep(milliseconds) 方法。
TERMINATED(终止) :线程执行完毕或因异常退出时,线程处于终止状态。
在 Java 中,通过调用 getState() 方法可以获取线程的当前状态,返回的是 Thread.State 枚举类型。
这里需要注意:NEW状态时,说明线程还未启动,其他状态都表示线程已经启动了或启动后执行结束了。
Thread 类提供了一些常用的方法来管理线程的执行和控制。以下是 Thread 类常见方法及其作用:
start() :启动线程,使线程进入就绪(runnable)状态,当获取到 CPU 时间片时开始执行线程的 run() 方法。
run() :线程的执行逻辑,需要在子类中重写这个方法来定义线程的任务。
sleep(long millis) :让当前线程休眠指定的时间(以毫秒为单位),线程进入阻塞状态,不会释放锁。
join() :等待调用该方法的线程执行完毕,当前线程会被阻塞,直到目标线程执行完毕。
interrupt() :中断线程,给该线程发出一个中断信号,线程可以在合适的时间响应中断。isInterrupted() 和 interrupted():
isInterrupted() :检查当前线程是否被中断,不会清除中断状态。
interrupted() :静态方法,检查当前线程是否被中断,会清除中断状态。
yield() :暂停当前正在执行的线程,让 CPU 调度器重新选择其他线程执行,可能会提高其他线程的执行机会。
isAlive() :检查线程是否存活,即线程已经启动但尚未终止,返回 boolean 值。
setName(String name) 和 getName() :
setPriority(int priority) 和 getPriority() :
getState() :返回线程状态
关于线程的常见方法,很多人都不太关注,这可是有很多朋友面试被问到过的。
聊了那么多线程相关知识,终于来到了今天的话题:线程是否已经启动?
下面,我用三种方式来和大家探讨。
Thread.currentThread().isAlive()方法用于检查当前线程是否处于活动状态。如果线程已经启动且尚未终止,则返回true,否则返回false。
Thread thread = new Thread(() -> {
// 线程执行的任务
});
// 判断线程是否启动
if (thread.isAlive()) {
System.out.println("线程已启动");
} else {
System.out.println("线程未启动");
}
通过调用getState()方法可以获取线程的状态,常见的状态包括NEW(新建)、RUNNABLE(可运行)、TIMED_WAITING(计时等待)等。
Thread thread = new Thread(() -> {
// 线程执行的任务
});
// 获取线程状态
Thread.State state = thread.getState();
if (state == Thread.State.NEW) {
System.out.println("线程尚未启动");
} else {
System.out.println("线程已启动");
}
在线程类中添加一个布尔变量或状态标记,来表示线程是否已经启动或结束。
class MyThread extends Thread {
private boolean isStarted = false;
@Override
public void run() {
isStarted = true;
// 执行任务
}
public boolean isThreadStarted() {
return isStarted;
}
}
MyThread thread = new MyThread();
thread.start();
// 判断线程是否启动
if (thread.isThreadStarted()) {
System.out.println("线程已启动");
} else {
System.out.println("线程未启动");
}
本文从线程与进程开始聊,再聊到了线程的创建方式、线程的状态、线程常见方法,最后再来聊了如何判断线程是否已经启动。希望这种思路能让你更轻松的掌握这个问题,另外,强烈建议在学技术时,最好是把技术回归到我们生活案例中。