1.1 思路整理
(1)继承Thread类
为什么要继承Thread类?
因为Thread类是描述线程事物的,具备线程应该有的功能。为什么不直接创建线程对象然后调用start方法呢?
Thread t = new Thread();
t.start();
这样做没有错,但是如果这样写的话,start方法调用的是Thread类中的run方法,而这个线程类中的run方法没有做什么事情,更重要的是这个run方法中并没有定义我们需要让线程执行的代码。
(2)重写run方法
因为线程类的run()方法我不需要,既然Thread类已经定义了线程任务的位置run()方法,只要在run()中定义任务代码就可以,所以就进行了重写run方法的操作。
(3)创建子类对象,就是创建线程对象
创建线程的目的是什么?
子类继承了Thread类,就具备了线程的功能。创建线程是为了建立单独的执行路径(多线程),让多部分代码实现同时执行,简单来说就是创建线程并执行给定的代码(线程任务)。
对于主线程,它的线程任务定义在了main方法中,所以主线程一启动就去指定的位置执行任务。而自定义的线程,需要执行的任务都定义在run方法中,而Thread类提供的run方法内部不是我们所需要的,
(4)调用start方法
开启线程并且执行,同时会告诉jvm去调用run方法。
1.2 多线程的运行内存图
多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间进行方法的压栈和弹栈。当执行线程(包括主线程)的任务结束了,线程自动在栈内存中释放。主线程和子线程是并列的,主线程执行结束会进行弹栈,与子线程无关。当所有的执行线程都结束了,进程就结束了。
1.3 代码示例
package com.java.thread;
public class Thread01 {
// 主线程
public static void main(String[] args) {
Demo d1 = new Demo("小明");
Demo d2 = new Demo("小强");
//创建线程对象后,要先开启线程,才能使d1.run和d2.run方法同时执行
d2.start();//将d2线程开启,并且去调用run方法
d1.run();//由主线程负责
}
}
// 继承Thread类
class Demo extends Thread{
private String name;
// 构造函数
public Demo(String name){
this.name = name;
}
// 重写run方法
public void run(){//线程的任务
for(int x=1;x<=20;x++){
//Thread.currentThread().getName():获取当前执行线程的名称,当线程多个时,数字顺延
System.out.println("name="+name+"\t线程名字:"+
Thread.currentThread().getName());
}
}
}
1.4 调用方法区别
调用run()方法和调用start()方法的区别:
调用run()方法不能开启线程,仅仅是对象调用普通方法。
调用start()方法开启线程,并让jvm调用run()方法在开启的线程中执行。
1.5 线程的异常
哪个线程出现异常,就抛出哪个线程的异常。线程的异常不影响其他线程的执行。