java thread start 多次_面试官:Thread启动线程的start方法能执行多次吗?

线程的创建

我们知道在Java里线程是通过java.lang.Thread类来实现的。一般我们创建无返回值的线程会用下面两个方法:继承Thread类,重写run()方法;

实现Runnable接口,重写run()方法;

线程启动会通过调用start方法来启动线程而不能直接调用run方法。

这里就会引出两个经典的面试题:为什么线程启动是调用start方法来启动线程而不能直接调用run方法?

如果多次调用start方法会发生什么?

其实答案就是源码里,在这之前我们要了解线程的状态有哪些。Java架构度寒冬​zhuanlan.zhihu.com9d4a192c4ca95dc11a8f40d0dd450626.png需要更多Java面试资料和学习干货可以关注我,我的专栏【Java架构度寒冬】,本专栏会长期更新java架构技术以及心得等精彩文章

线程的状态

线程从创建到死亡是会经历多个状态的流转的。它们分别是:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。

Java-Thread-Status

// Thread类的内部的枚举类定义了线程的状态

public enum State {

NEW,

RUNNABLE,

BLOCKED,

WAITING,

TIMED_WAITING,

TERMINATED;

}

start() 方法 & run()方法

为什么线程启动是调用start方法来启动线程而不能直接调用run方法?

从上图中,我们就可以看到,在New了一个线程后,首先进入初始态,然后调用start()方法来到就绪态,这里并不会立即执行线程里的任务,而是会等待系统资源分配,当分配到时间片后就可以开始运行了。 start()方法是一个native方法,它将启动一个新线程,并执行run()方法,这是真正的多线程工作。 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。这就是为什么调用 start() 方法时会执行 run() 方法,为什么不能直接调用 run() 方法的原因了。

Example:

public class ThreadDemo

public static void main(String[] args) {

Thread t1 = new Thread(new Task1());

Thread t2 = new Thread(new Task2());

// 测试1

t1.start();

t2.start();

// 测试2

t1.run();

t2.run();

}

}

class Task1 implements Runnable {

@Override

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println("Task1: " + i);

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

class Task2 implements Runnable {

@Override

public void run() {

for (int i = 10; i > 0; i--) {

System.out.println("Task2: " + i);

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

// 测试1输出

Task1: 0

Task2: 10

Task1: 1

Task2: 9

Task1: 2

Task2: 8

Task1: 3

Task2: 7

Task1: 4

Task2: 6

Task1: 5

Task2: 5

Task1: 6

Task2: 4

Task1: 7

Task2: 3

Task1: 8

Task2: 2

Task1: 9

Task2: 1

我们可以看到Task1 和 Task2是交替打印的,是多线程在运行。

// 测试2输出

Task1: 0

Task1: 1

Task1: 2

Task1: 3

Task1: 4

Task1: 5

Task1: 6

Task1: 7

Task1: 8

Task1: 9

Task2: 10

Task2: 9

Task2: 8

Task2: 7

Task2: 6

Task2: 5

Task2: 4

Task2: 3

Task2: 2

Task2: 1

这个的输出是串行的,Task1 执行完才执行 Task2,所以不是多线程,是普通方法。

如果多次调用start方法会发生什么?

首先我们先测试一下。

Example:

public class ThreadDemo

public static void main(String[] args) {

Thread t1 = new Thread(new Task1());

Thread t2 = new Thread(new Task2());

// 测试3

t1.start();

t1.start();

}

}

// 测试3输出

Task1: 0

Task...

Exception in thread "main" java.lang.IllegalThreadStateException

at java.lang.Thread.start(Thread.java:708)

Task...

只有第一次成功执行,后面就抛出了异常

java.lang.IllegalThreadStateException,让我们来从下面的源码里看看吧,在start方法进来后就会判断线程的状态,如果不是初始态状态就会抛出异常,所以第二次执行就会报错,因为线程的状态已经发生改变。

源码

start()方法源码:

public synchronized void start() {

// 如果线程不是"NEW状态",则抛出异常!

if (threadStatus != 0)

throw new IllegalThreadStateException();

// 将线程添加到ThreadGroup中

group.add(this);

boolean started = false;

try {

// 通过start0()启动线程,新线程会调用run()方法

start0();

// 设置started标记=true

started = true;

} finally {

try {

if (!started) {

group.threadStartFailed(this);

}

} catch (Throwable ignore) {

}

}

}

run方法源码:

public void run() {

if (target != null) {

target.run();

}

}

总结start()方法是用来启动线程,真正实现了多线程运行。

run()方法是一个普通方法。

调用start()方法后会先判断线程的状态是否为NEW,所以线程只能启动一次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值