虽然Thread类实现了Runnable接口,但是操作线程的主要方法并不在Runnable接口中,多线程操作,主要是通过Thread类中的方法实现。
1
线程的生命周期
要想实现多线程,必须在主线程中创建新的线程对象。任何线程一般具有5种状态:创建、就绪、运行、阻塞、终止。
线程状态的变化与方法之间的关系如下图:
01、创建状态(NEW)
新建一个线程对象后,新的线程对象就处于新建状态,此时它处于不可运行状态。新建一个线程对象使用Thread类的构造方法来实现。
Thread Thread = new Thread();
02、就绪状态(RUNNABLE)
新建的线程对象调用了线程的start方法就可以启动线程,当线程启动后,线程就进入就绪状态。此时,线程进入线程队列排队,等待CPU的调度。
03、运行状态(RUNNING)
当就绪状态的线程被CPU调用,并获得处理器资源时,线程就进入了运行状态。此时,系统会自动调用该线程对象的run方法。run方法定义了该线程的具体操作和功能。
04、阻塞状态(BLOCKED)
一个正在运行的线程在被人为挂起或者需要执行耗时的输入、输出操作时,将让出CPU资源,并暂时中止该线程的执行,进入阻塞状态。
在线程运行状态下,如果人为调用sleep方法、suspend方法、wait方法,线程就会进入阻塞状态,阻塞的线程会进入阻塞队列,只有当引起阻塞的事件解除后,线程才可以进入就绪状态,继续等待CPU的调度。
05、死亡状态(TERMINATED)
线程调用stop方法或者run方法执行结束后,即处于死亡状态,处于死亡状态的线程不再具有继续运行的能力,即该线程对象终止了。
2
线程操作的八个方法
线程的创建是用Thread类的构造方法,因此线程操作的主要方法是Thread类中的方法。
01、设置和获取线程名称
除了可以通过Thread类的构造方法设置线程名称之外,还可以通过setName方法设置线程名称。
获取当前线程名称的代码:
Thread.currentThread().getName();
示例1:在不自定义线程名称时,观察线程的默认名称的规律
public class Task implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName() + "运行,i=" + i);
}
}
}
// 测试类
public class TaskTest {
public static void main(String[] args) {
Task task = new Task();
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
}
}
程序运行结果:
Thread-0运行,i=1
Thread-2运行,i=1
Thread-1运行,i=1
Thread-0运行,i=2
Thread-1运行,i=2
Thread-2运行,i=2
Thread-1运行,i=3
Thread-0运行,i=3
Thread-2运行,i=3
通过以上的程序可以看出,如果没有设置线程的名称,系统会为其自动创建名称,名称的格式为Thread-数字(从0开始的自增值)
通过以下jdk中Thread类的源码可以证明以上的内容。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private static int threadInitNumber; // 静态的变量,用于匿名线程的自增
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
示例2:使用setName方法设置线程名称,并获取线程名称
// 测试类
public class TaskTest {
public static void main(String[] args) {
Task task = new Task();
Thread thread = new Thread(task);
thread.setName("自定义线程"); // 使用setName方法设置线程名称
thread.start();
}
}
程序运行结果:
自定义线程运行,i=1
自定义线程运行,i=2
自定义线程运行,i=3
02、判断线程是否启动
通过Thread类中的start方法通知CPU这个线程已经准备好了,等待分配CPU资源后运行线程。
在Java中可以使用isAlive方法来判断线程是否已经启动。
示例1:判断线程是否启动
// 测试类
public class TaskAliveTest {
public static void main(String[] args) {
Tas