深入线程Thread类的start()方法和run()方法
一、初识
Java 线程是通过 java.lang 实现的。班级。当VM启动时,main方法定义了一个线程。可以通过创建实例来创建新线程。每个线程通过特定对象对应的方法run()来完成自己的操作,方法run()称为线程体。通过调用类的 start() 方法启动线程。
在Java中,线程通常有五种状态,创建、就绪、运行、阻塞和死亡。
首先是创建一个状态。线程对象生成时,并没有调用该对象的start方法,也就是正在创建的线程。
第二个是就绪状态。当调用线程对象的start方法时,线程进入就绪状态,但是线程调度器此时还没有将线程设置为当前线程,此时处于就绪状态。线程运行后,从等待或休眠返回后也处于就绪状态。
第三个是运行状态。线程调度器将处于就绪状态的线程设置为当前线程。此时线程进入运行状态方法,开始运行run函数中的代码。
第四是阻塞状态。当一个线程在运行时,它会被挂起,通常是等待一定的时间发生(例如,一个资源准备好),然后再继续运行。 sleep、wait等方法都会导致线程阻塞。
第五个是死亡状态。如果一个线程的 run 方法结束或 stop 方法被调用,线程就会死掉。对于已经死亡的线程,不能再使用 start 方法使其准备就绪。
二、start() 方法
1、为什么需要start方法;它有什么作用?
start()方法启动线程,真正实现了多线程操作。
角色
start方法是将线程从NEW状态改变为状态。当线程创建成功时,线程处于NEW(新)状态。如果不调用 start() 方法,线程始终处于 NEW 状态。调用start()后,变为状态,线程可以运行了。
2、调用start()方法后,线程是否立即执行?
线程不会立即执行;准确的说,调用start()方法后,线程的状态是“READY(就绪)”状态,而不是“(运行)”状态(具体说下线程的状态。等待CPU调度,不同JVM有不同的调度算法,线程什么时候被调度是未知的,所以start()方法的调用顺序不能决定线程的执行顺序
注意:
因为在线程的生命周期中,线程的状态只会发生一次NEW ---->,因此,一个线程只能调用一次start()方法,启动一个线程是非法的线程多次。特别是当线程执行完毕后,就无法重新启动了。
三、run()方法
1、run方法是什么方法? run方法和start方法有什么关系?
run() 方法作为普通方法调用
run()其实是一个常用的方法,但是当线程调用start()方法时,一旦线程被CPU调度并运行,线程就会调用run()方法;
2、run()方法的执行是否需要线程调用start()方法?
如前所述,run()方法是一个普通的对象方法,所以可以在没有线程调用start()的情况下调用它。线程对象可以随时随地调用run方法。
#:
Thread t1 = new Thread(new MyTask(1));
Thread t2 = new Thread(new MyTask(2));
t1.run();
t2.run();
复制
上面的输出是固定的:
计数值:1
计数值:2
再看一个例子:
Thread t1 = new Thread(new MyTask());
Thread t2 = new Thread(new MyTask());
t1.start();
t2.start();
复制
这个输出不是固定的方法,因为线程的执行是不可预测的。运行结果可能不同。
类:
//实现Runnable接口
class MyTask implements Runnable{
int count;
public MyTask(int count) {
this.count=count;
}
@Override
public void run() {
System.out.println("count的值:"+count);
}
}
复制
#:
1、使用start方法启动线程
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
t1.start();
t2.start();
}
}
class T1 implements Runnable {
public void run() {
try {
for(int i=0;i<10;i++){
System.out.println(i);
Thread.sleep(100); //模拟耗时任务
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class T2 implements Runnable {
public void run() {
try {
for(int i=0;i>-10;i--){
System.out.println(i);
Thread.sleep(100); //模拟耗时任务
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制
结果:
在这里写图片说明
表示两个线程同时执行。
2、使用run方法先启动线程
把上面的start()改成run()
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
t1.run();
t2.run();
}
}
复制
在这里写图片说明
表示这两个线程实际上是按顺序执行的。
总结:
通过 1和 sum可以知道start方法是用来启动线程的,可以实现并发,而run方法只是一个普通的方法,不能实现并发,但是会在并发执行的时候被调用。
说到这里,不知道各位朋友是否明白这两种方式的区别。有问题可以留言交流。
四、start()方法和run()方法源码分析(基于JDK1.7.0_40)
public synchronized void start() {
// 如果线程不是"就绪状态",则抛出异常!
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) {
}
}
}
复制
public void run() {
if (target != null) {
target.run();
}
}
复制
五、真正懂类
类的对象其实是一个java对象,但是类的每个对象都对应一个线程。类的对象是提供给用户操作线程和获取线程的信息。真正的低级线程用户是不可见的。
因此,当一个线程结束并死亡时,仍然可以调用对应的对象,除了start()方法(死掉的线程不能再次启动),例如run()、()、()等。
//简单起见,使用匿名内部类的方法来创建线程
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("Thread对象的run方法被执行了");
}
};
//线程启动
thread.start();
//用循环去监听线程thread是否还活着,只有当线程thread已经结束了,才跳出循环
while(thread.isAlive()){}
//线程thread结束了,但仍能调用thread对象的大部分方法
System.out.println("线程"+thread.getName()+"的状态:"+thread.getState()+"---优先级:"+thread.getPriority());
//调用run方法
thread.run();
//当线程结束时,start方法不能调用,下面的方法将会抛出异常
thread.start();
复制
参考文献