Java--多线程03
可以看到我们有两个线程,一个是yaya,一个是主线程main,正常情况下我们应该是需要两个线程一起跑的,但是这里我们针对yaya没有调用start方法让它跑起来,也就是说yaya无法执行!就绪队列:[线程1, 线程2, 线程4, 线程5, 线程6, 线程7, 线程9, 线程10, 线程11, 线程12, 线程13, 线程14, 线程16, 线程17, 线程18, 线程19]这里我们换成了for循环
1. 线程的关键属性
1.1 Thread的常见构造方法

1 Thread t1 = new Thread();
2 Thread t2 = new Thread(new MyRunnable());
3 Thread t3 = new Thread("这是我的名字");
4 Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
1.2 Thread 的几个常见属性
我们先来看一下几个常用的方法,稍后会在代码中使用到~

- ID是线程的唯一标识,不同线程不会重复
- 名称是各种调试工具中会遇到可以自己起名也可以自动起名
- 状态标识线程当前所处的一个情况,下面会讲到
- 优先级高的线程在理论上来说更容易被调度到(线程是随机调度)
- 关于后台线程,只需要记住一点:JVM在一个线程的所有非后台线程结束之后,才会结束运行
- 是否存活最简单的理解就是run方法是否运行结束
- 线程的中断问题在下面会逐步详解
这里对于部分属性选择在线程可视化界面中进行观察~
1.2.1 ID
首先我们需要找到jconsole,在前言中有简介
然后我们在IDEA中去执行一个多线程的代码,以下图为例:
运行起来之后进入jconsole程序(进入自己创建的线程)
选中后直接点连接

进入后选择不安全的连接

进入后点击上方的线程选项

点击线程之后如下图:
以上就是线程的ID了~~
代码:
public class demo6 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true){
System.out.println("hello thread");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
while (true){
System.out.println("hello main");
Thread.sleep(3000);
}
}
}
1.2.2 名称
在上面ID的简介中,我们提到了一个子线程的默认名称:Thread-?(0开始,0,1,2.......)
我们在使用lambda表达式的时候可以给线程起一个名字:
public class demo6 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true){
System.out.println("hello thread");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"我的子线程");
t.start();
while (true){
System.out.println("hello main");
Thread.sleep(3000);
}
}
}
现在起好名称后我们运行程序,然后重启一下jconsole再去线程中看看名称是Thread-0还是我的子线程
显而易见,我们的名称修改成了~
1.2.3 状态
Java中线程一般有以下状态:
-
NEW(新建)
线程被创建,但尚未调用start()方法。 -
RUNNABLE(可运行)
线程正在运行或准备运行(等待CPU时间片)。
注意:在操作系统层面,此状态可能对应 就绪(Ready) 和 运行(Running) 两种状态。 -
BLOCKED(阻塞)
线程因等待监视器锁(如synchronized)而阻塞,只有进入synchronized代码块时才会进入此状态。 -
WAITING(等待)
线程因调用wait()、join()、LockSupport.park()等方法进入无限期等待,需要其他线程显式唤醒。 -
TIMED_WAITING(计时等待)
线程进入有限时间的等待(如sleep(ms)、wait(timeout)、join(timeout))。 -
TERMINATED(终止)
线程执行完毕或异常退出。
本篇先只讲前两个
1.2.3.1 NEW(新建)
这个状态就是线程被我们创建了,但是并没有调用start方法让线程跑起来~
来看个代码理解一下~
public class demo2 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true){
System.out.println("hello thread");
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}"yaya");
while (true){
System.out.println("hello main");
Thread.sleep(1000);
}
}
}
可以看到我们有两个线程,一个是yaya,一个是主线程main,正常情况下我们应该是需要两个线程一起跑的,但是这里我们针对yaya没有调用start方法让它跑起来,也就是说yaya无法执行!!
这里我们把代码做一些改动,再去观察一下~:
public class demo2 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for(int i = 0; i < 3; i++){
System.out.println("hello thread");
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"yaya");
for (int i = 0; i < 3; i++) {
System.out.println("hello main");
Thread.sleep(1000);
}
}
}
这里我们换成了for循环,让yaya执行三次,main执行三次,但是我们没有让yaya跑起来,我们应该看到的是main执行三次
可以看到,结局是意料之中的~

与此同时,我们在线程监视器中也看不到yaya的影子~
在我们以后写多线程相关的代码时,我们需要注意这种情况,不过也可以利用这个特性去完成一些特殊的工作~
加餐
相信看到这里很多人会想:为什么会出现我们明明已经创建了yaya这个线程,而在线程监视器上却看不到yaya这样的情况呢?这里来做一个解答:
线程的创建过程是分阶段的:
Thread t = new Thread(() -> System.out.println("Hello")); // 此时只是Java对象
// t.start(); // 没有调用start(),操作系统层面没有创建真正的执行实体
- new Thread()只是在JVM的堆内存中创建了一个Java对象
- start()才会调用操作系统API创建真正的系统线程!
Java层: Thread对象(NEW状态)
↓ start()调用
操作系统层: 创建内核线程/轻量级进程(分配TCB、栈空间等)
↓ 进入就绪队列
监视器可见
比如我们来一个案例更直观:在这里面我们创建1000个线程,但是start10个线程,然后打印所有线程
import java.util.ArrayList;
import java.util.List;
public class demo2 {
public static void main(String[] args) throws Exception {
// 创建1000个线程但不启动
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
threads.add(new Thread(() -> {
try {
Thread.sleep(60000); // 睡1分钟
} catch (Exception e) {}
}, "Thread-" + i));
}
System.out.println("创建了 " + threads.size() + " 个线程");
System.out.println("按回车启动前10个线程...");
System.in.read();
// 只启动前10个线程
System.out.println("启动前10个线程...");
for (int i = 0; i < 10; i++) {
threads.get(i).start();
}
// 简单打印所有线程状态
System.out.println("\n=== 线程状态总览 ===");
int started = 0;
int notStarted = 0;
for (int i = 0; i < threads.size(); i++) {
Thread t = threads.get(i);
if (t.isAlive()) {
started++;
if (started <= 10) { // 只显示前10个启动的线程详情
System.out.println(t.getName() + " | 状态: " + t.getState() + " | 存活");
}
} else {
notStarted++;
}
}
System.out.println("\n=== 统计 ===");
System.out.println("已启动线程数: " + started);
System.out.println("未启动线程数: " + notStarted);
System.out.println("总线程数: " + threads.size());
System.out.println("\n现在可以运行 jstack 查看线程状态");
System.out.println("主线程将等待60秒...");
Thread.sleep(60000);
}
}

可以看到我们只start10个线程,打印出来的线程只有10个,剩下的990个都是未启动的
于是得出结论:
- 创建线程对象≠启动线程
- 线程在调用start()之前只是普通的对象
- 在JVM工具中也是只有调用start()后,线程才会被JVM接管
JVM工具获取的是活动线程,而不会获取到new状态的线程!!
1.2.3.2 RUNNABLE(可运行)
Runnable分为两种情况:1.真正的在CPU上在运行。 2.就绪状态,等待CPU执行。这里给这两种情况分别举例
第一种情况:在执行:
public class RealRunning {
public static void main(String[] args) {
// 这个线程会真正占用CPU
Thread t = new Thread(() -> {
System.out.println("开始计算");
long sum = 0;
for (long i = 0; i < 10000000; i++) {
sum += i;
}
System.out.println("计算完成");
});
t.start();
// 主线程监控
while (t.isAlive()) {//isAlive是检测线程是否存活,虽在后面讲,但此处要用到
System.out.println("线程状态: " + t.getState()); // 一直显示RUNNABLE
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}

在计算过程中,线程的状态是Runnable。这里的Runnable是正在执行的状态
第二种:就绪状态:
public class ss {
public static void main(String[] args) throws Exception {
System.out.println("CPU核心数: " + Runtime.getRuntime().availableProcessors());
// 创建大量一直计算的线程(不sleep)
int threadCount = 20; // 远超CPU核心数
for (int i = 1; i <= threadCount; i++) {
final int id = i;
new Thread(() -> {
System.out.println("线程" + id + "启动,将一直计算");
// 一直计算,不sleep,不让出CPU
long counter = 0;
while (true) {
counter++;
if (counter % 100000000 == 0) {
System.out.println("线程" + id + "仍在计算... 状态: RUNNABLE");
}
}
}, "Compute-" + i).start();
Thread.sleep(50); // 错开启动时间
}
// 监控
Thread.sleep(1000);
System.out.println("\n=== 当前状态 ===");
System.out.println("20个线程都在RUNNABLE状态");
System.out.println("但只有" + Runtime.getRuntime().availableProcessors() +
"个能真正运行");
System.out.println("其他在操作系统就绪队列等待CPU");
}
}
这个就是一个典型的例子了~不过不建议运行哦~风扇会疯狂转,如果好奇可以试试(及时停止)~
不说废话了,现在开始讲原理:
CPU核心数: 4(假设是4核电脑)
线程1启动,将一直计算
线程2启动,将一直计算
...
线程20启动,将一直计算
===状态===
20个线程都在RUNNABLE状态
但只有4个能真正运行
其他在操作系统就绪队列等待CPU
Java层面(你看到的):
-
所有20个线程:
Thread.getState()都返回RUNNABLE -
看起来:20个线程都在"运行"
操作系统层面(实际发生的):
CPU核心0:正在执行 线程3 CPU核心1:正在执行 线程8 CPU核心2:正在执行 线程15 CPU核心3:正在执行 线程20 就绪队列:[线程1, 线程2, 线程4, 线程5, 线程6, 线程7, 线程9, 线程10, 线程11, 线程12, 线程13, 线程14, 线程16, 线程17, 线程18, 线程19]
-
只有4个线程在真正占用CPU
-
其他16个都就绪队列排队
为什么都显示RUNNABLE?
Java的线程状态是逻辑状态:
-
RUNNABLE= "我可以运行" -
不区分:"正在运行" vs "就绪等待"
这就好比:
-
电影院检票口:20个人都拿着票(RUNNABLE)
-
但只有4个检票口(CPU核心)
-
16个人在排队(就绪队列),但他们都有票(显示RUNNABLE)
就绪状态的本质就是——有资格运行,但是还没轮到
更多推荐

所有评论(0)