目录
前言
如何简单的去使用jconsloe 查看线程 (多线程编程篇1)_如何查看weblogic当前线程-CSDN博客
距离笔者发布本系列第一篇博客已过去一月有余,本篇博客,笔者将继续介绍已经整理学习好的内容
既帮助未来的自己复习,也为正在阅读的你提供参考和思考.
本篇博客的内容正如标题所示,笔者主要将介绍一下内容
1.Thread类,并通过举例演示常见方法
2.向大家介绍线程的状态.
愿我们一起进步!
1.Thread类及常见方法
首先,需要说明的是,相比于多进程编程,多线程编程能够更有效地利用多核CPU资源。
线程作为进程中的执行单元,它们共享进程的内存空间,因此在创建、调度和销毁时所消耗的资源远少于进程。由于线程之间的开销较小,因此在多个线程之间的调度和切换效率较高。
Thread类中常见的属性
如何简单的去使用jconsloe 查看线程 (多线程编程篇1)_如何查看weblogic当前线程-CSDN博客
1. getId()
功能:获取线程的唯一标识符(ID),和PID类似。每个线程都有一个唯一的 ID,它是一个正整数。这个ID是JAVA给分配的,不是系统API提供的,更不是PCB中的ID.
用途:可以用来标识不同的线程。
2. getName()
功能:获取线程的名称。每个线程都有一个名字,默认是
Thread-0
、Thread-1
等,当然也可以通过构造方法手动设置线程名称。用途:帮助开发者区分不同的线程
3. getState()
功能:获取线程的状态,返回一个
Thread.State
枚举值。用途:用于查看线程的当前状态
4. getPriority()
功能:获取线程的优先级。
用途:用于控制线程的调度顺序,优先级高的线程会比优先级低的线程更早得到 CPU 时间。
5. isDaemon()
功能:检查线程是否为守护线程(也可以叫做后台线程)(Daemon thread)。守护线程是辅助性线程,在所有非守护线程结束后会自动退出。
用途:判断线程是否为后台线程。守护线程一般用于系统服务或后台任务,如垃圾回收线程。
一般情况下,我们会默认一个线程为前台线程,一个JAVA进程中,如果前台线程没有执行结束,那么整个进程是一定不会结束的,但是后台进程是否结束了,不会影响整个进程的进行
举一个简单的例子 :
public class Demo2
{
public static void main(String[] args) {
Thread y = new Thread(() -> {
while (true) {
System.out.println("Test");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}, "线程一"); // lambda 表达式写法
y.setDaemon(true); // 设置为后台线程
y.start(); // 启动线程
}
}
如果在这里设置成后台线程,那么运行结果如下
进程已结束,退出代码0
或者
Test
进程已结束,退出代码0
如果y是一个前台线程,那么该程序应该一直打印"Test",但是我们在这里把他设置为了后台线程,
此时主线程立马就执行完了,由于没有其他前台线程了,因此进程结束了,y线程来不及执行,或者只能执行几次.
6. isAlive()
功能:检查线程是否处于活动状态。如果线程已经启动并且还没有终止,则返回
true
,否则返回false
。用途:用于判断一个线程是否仍然在运行。
举个例子
public class Demo2
{
public static void main(String[] args) throws InterruptedException {
Thread y = new Thread(() -> {
System.out.println("Test");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}, "线程一"); // lambda 表达式写法
y.start(); // 启动线程
System.out.println(y.isAlive());
Thread.sleep(2000);
System.out.println(y.isAlive());
}
}
结果如下:
true
Test
false
进程已结束,退出代码0
启动y线程后,线程存活,等待两秒后,线程的活动被终止
7. isInterrupted()
功能:检查线程是否被中断。如果线程被中断,返回
true
,否则返回false
。用途:判断线程是否接收到中断信号。
2.Thread类中常见的方法
Thread.interrupt() (中断线程)
作用:中断线程的执行。
用途:通过调用该方法来中断一个线程的执行,线程可以通过
interrupted()
或isInterrupted()
方法判断是否被中断。
举个例子
结合我们刚刚提到的isInterrupted(),我们举一个例子
下面是一个线程执行的示例,线程会不断打印 "正在工作"
,直到被 interrupt()
发送中断信号后,它会检查自身的 isInterrupted()
状态并退出。
public class Demo4 {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("正在工作");
try {
Thread.sleep(1000); // 线程进入休眠状态
} catch (InterruptedException e) {
System.out.println("线程被中断,抛出异常终止");
// 如果选择抛出异常,线程会立即终止
// 当捕获到 InterruptedException 时,异常被重新抛出,
// 这会导致当前线程的执行停止,不会继续执行 while 循环后面的代码。
// 也就是说,抛出 RuntimeException 可能会导致线程提前结束。
// throw new RuntimeException(e);
// ✅ 如果使用 break,则线程可以优雅退出
// break;
}
}
// ⚠ 只有在没有抛出异常时,这行代码才会执行
System.out.println("工作结束");
});
thread.start();
try {
Thread.sleep(3000); // 主线程等待 3 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // 发送中断信号
System.out.println("已通知线程中断");
}
}
值得注意的就是这里要不要 throw new RuntimeException(e)
而关于Thread.currentThread()
在 Java 中,Thread.currentThread()
返回当前正在执行的线程对象。因为 thread
变量指向的是主线程创建的线程实例,而 Thread.currentThread()
总是指向当前正在运行的线程,因此在线程内部调用 Thread.currentThread().isInterrupted()
可以正确获取线程的中断状态。
并且,即使线程内部的逻辑出现阻塞,也可以用这个方法唤醒,正常来说,sleep会休眠到时间到了才唤醒,但是interrupt()方法 可以使sleep内部出发异常,从而被提前唤醒,如果我们自己定义标注为,就做不到
效果如下:
但是sleep方法抛出异常,同时也会自动清除刚才设置的标志位,所以我们的线程会继续工作,所以我们需要break优雅地去中断.
我们再总结一下
1.如果只捕获异常,不做处理
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); // 仅打印异常
}
标志位会被清除,但是线程不会停止,将继续工作
2.使用break;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); // 仅打印异常
break;
}
线程不会抛出异常,而是优雅退出循环。
while
结束后,线程会执行"工作结束"
语句并结束。
3.你选择抛出异常
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
线程会立即终止,不会执行
while
循环后面的代码。"工作结束" 也不会被打印,因为
while
之后的代码永远不会执行。
读者们可以把代码拷到自己的环境里去试验一下,笔者下的结论建立在试验过的基础之上.
Thread.start()(启动线程)
这里笔者主要是谈一下覆写RUN方法和调用start()方法的区别,它们有什么区别呢?
1. 覆写 run()
方法
run()
方法是 Thread
类的一个普通方法,如果我们直接调用 run()
,它不会启动一个新的线程,而是作为当前线程中的一个普通方法执行。例如:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程正在执行: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.run(); // 直接调用 run()
System.out.println("主线程执行完毕");
}
}
效果如下:
可以看到,run()
方法是在 主线程 中执行的,而不是在一个新的线程中。
2.调用start()
start()
方法是 Thread
类的核心方法,调用 start()
后,JVM 会创建一个新的线程,并调用该线程的 run()
方法,从而实现真正的并发执行。例如:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程正在执行: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // 启动新线程
System.out.println("主线程执行完毕");
}
}
代码效果:
主线程执行完毕
线程正在执行: Thread-0
进程已结束,退出代码0
通俗来说,假设我是经理,我把我的下属员工张三喊过来,我告诉张三他这周的工作任务现在,我让他行动起来.
在这段文字中:
"我是经理" → 你是主线程 (
main
线程)。"把张三喊过来" → 你创建了一个线程对象 (
new Thread()
)。"我告诉张三他的工作任务" → 你覆写了
run()
方法,定义了线程要执行的内容。"我让张三行动起来"==
start()
→ 你调用了start()
方法,让张三真正开始工作,并且他是独立工作的,你自己(主线程)也可以去做别的事了。"如果我只是告诉张三他的工作任务,而没让他行动"==
run()
→ 你只是调用了run()
方法,这样张三不会真正开始工作,而是你自己(经理)亲自去做他的工作(主线程执行run()
里面的内容)。
Thread.join()(等待线程)
在 Java 线程编程中,join()
方法用于让当前线程等待另一个线程执行完成,然后才继续执行。它的作用是同步线程,确保某个线程执行完毕后,其他线程才能继续运行。
例如现在有一个线程t,如果主线程调用t.join(),那么主线程就会进入阻塞状态,等待t线程先执行完
笔者再给一个示例代码:
class Worker extends Thread {
private String name;
public Worker(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name + " 开始工作...");
try {
Thread.sleep(2000); // 模拟任务执行 2 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + " 工作完成!");
}
}
public class JoinExample {
public static void main(String[] args) {
Worker worker1 = new Worker("张三");
Worker worker2 = new Worker("李四");
worker1.start();
worker2.start();
try {
worker1.join(); // 等待 worker1 执行完
worker2.join(); // 等待 worker2 执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有工作完成,主线程继续执行!");
}
}
效果如下:
张三 开始工作...
李四 开始工作...
张三 工作完成!
李四 工作完成!
所有工作完成,主线程继续执行!
又或者,我们希望多个线程按照一定顺序进行,也可以使用join()方法
例如:
public class JoinExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
System.out.println("线程 1 执行");
});
Thread thread2 = new Thread(() -> {
System.out.println("线程 2 执行");
});
Thread thread3 = new Thread(() -> {
System.out.println("线程 3 执行");
});
try {
thread1.start();
thread1.join(); // 等待 thread1 结束
thread2.start();
thread2.join(); // 等待 thread2 结束
thread3.start();
thread3.join(); // 等待 thread3 结束
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有线程执行完毕!");
}
}
在main线程中那个线程调用了join()方法, main线程就一定要等待该线程执行完,然后才能接着执行
效果如下:
线程 1 执行
线程 2 执行
线程 3 执行
所有线程执行完毕!
感兴趣的读者们可以把代码拷到自己的环境中去尝试 !
3.线程的状态
我们如果想涉及多线程的安全问题, 那我我们就必须先知道多线程有哪些状态?
前面我们提到过 getState() 可以获取当前线程的状态
public class ThreadState {
public static void main(String[] args) {
// 遍历 Thread.State 枚举中的所有状态
for (Thread.State state : Thread.State.values()) {
// 输出每种状态
System.out.println(state);
}
}
}
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
进程已结束,退出代码0
1. NEW(新建)
线程刚刚被创建,还 没有启动,此时线程对象已经被分配内存,但还未执行
start()
方法。特征:未执行任何代码。
Thread thread = new Thread(() -> System.out.println("Hello"));
System.out.println(thread.getState()); // NEW
2. RUNNABLE(运行中)
线程已经 调用
start()
方法,并且 可能正在运行,也可能在等待 CPU 调度(就绪)。这个状态表示线程 可运行,但 不一定正在运行(因为可能在 CPU 时间片等待中)。
Thread thread = new Thread(() -> {
while (true) {} // 无限循环,保持线程存活
});
thread.start();
System.out.println(thread.getState()); // RUNNABLE
3. BLOCKED(阻塞)
线程 等待获取锁,但该锁 被其他线程持有,导致线程无法继续执行。
特征:只能用于同步代码块(
synchronized
),等待进入临界区。
4. WAITING(无限等待)
线程 无限期等待,直到 被其他线程显式唤醒(
notify()
或notifyAll()
)。特征:不会自动恢复,必须 由其他线程唤醒。
5. TIMED_WAITING(限时等待)
线程进入 限时等待状态,会在指定时间后自动恢复。
特征:等待 超时后自动唤醒,无需其他线程干预。
6. TERMINATED(终止)
线程执行完
run()
方法,或者 抛出未捕获异常 导致终止。线程生命周期结束,不能再
start()
。
结尾
写到这里,笔者就暂时停笔了,知识简单但是汇总不易,实验的结果都是笔者自己写代码自己试验出来的,希望对读者有用,可以的话,请您投个票