11_04.【Java】线程生命周期与线程操作

本文深入探讨Java线程的生命周期,包括出生、就绪、运行、阻塞和死亡5个状态,并详细讲解了创建、启动、休眠、加入、让步、中断和更改优先级等线程操作,结合实例解析了各种操作的实现逻辑和方法使用。
摘要由CSDN通过智能技术生成

本文从线程的生命周期及其对应的线程的5中状态入手,来详细讲解使线程从一个状态转变为另一种状态的常用操作方法,包括完成线程创建、线程启动、线程休眠、线程加入、线程让步、线程中断等操作需要使用的方法及其具体实现。

一、线程的生命周期

线程是具有生命周期的,其中包含5种状态:出生状态、就绪状态、运行状态、暂停状态(包括休眠状态、等待状态和阻塞状态)、死亡状态。

1、线程生命周期的5个状态

(1)出生状态

出生状态就是线程被创建时的状态,是指线程已经被创建但尚未被指定(start()尚未被调用)。

我们使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于出生状态。它保持这个状态直到程序 start() 这个线程。

(2)就绪状态

当线程对象调用了start()方法之后,该线程就进入就绪状态(也称为可执行状态)。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。这个状态下中的线程虽然不一定正在执行,但CPU时间随时可能被分配给该线程。

(3)运行状态

如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

(4)阻塞状态

阻塞状态:是指线程没有被分配到CPU时间,无法执行。如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

  • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
  • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
  • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

(5)死亡状态

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

正常情况下,run()方法返回使得线程死亡,这时候会产生异常。调用stop()或者destory()也有同样的效果,但是不推荐使用stop和destory,因为属于强制种植,不会释放锁。

2、线程生命周期状态图

在这里插入图片描述

也有一些资料将线程的声明周期划分为6个阶段:NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED,感兴趣的可自行查阅资料,我们就不在这里赘述了。本篇文章及后续文章中,本人还是统一将线程生命周期看作5个状态。

二、线程的操作

除了最基础的创建、启动线程,还有一些方法是使线程从一个状态转变为另一个状态的,包括包括线程的休眠、线程加入、线程让步、线程中断等操作。

1、创建线程

我们使用new关键字创建线程对象。创建线程的方式包括:通过Thread类重写run()方法实现;通过Runnable接口创建Thread对象并调用start()方法实现;通过Callable和Future创建线程三种方法。

使用new()方法创建线程对象,三种线程实现方法都会之间或间接的创建Thread对象。

new()
  • 继承 Thread 类
  • 实现 Runnable 接口
  • 通过Callable和Future创建线程

创建线程的三种方法的详细内容请参阅多线程的3种实现方式部分内容。

2、启动线程

(1)start()方法启动线程

三种方法创建的线程对象,都与Thread对象相关联:Thread类实现线程过程中会直接创建Thread对象;通过Runnable接口实现线程需要使用参数为Runnable对象的构造方法创建Thread对象;Callable和Future创建线程过程中,会使用FeatureTask对象作为Thread对象的target创建线程。三种方式都会创建直接或间接的与Thread对象相关联的对象,因此都能够通过调用Thread的start()方法来启动线程。

.start()
  • 调用 start() 方法

创建并启动线程的三种方法的详细内容请参阅多线程的3种实现方式部分内容。

(2)判断线程是否启动

使用isAlive()方法来判断线程是否已经启动而且仍然在启动

.isAlive()

(3)判断线程是否为启动状态

实例1:创建并启动线程,在线程启动前后调用isAlive()方法判断线程启动状态
*
实现Runnable接口的类
**/

//①首先需要创建一个实现Runnable接口的类,命名为RunnableDemo
class RunnableDemo implements Runnable {
   
	private Thread t;
	private String threadName;
  
  //②在RunnableDemo中使用参数为Runnable对象的构造方法创建Thread类
	public RunnableDemo(String name){
   
	  threadName = name;
	  System.out.println("创建"+threadName);
	}
	//③在实现类中重写Runnable接口的run方法
	public void run() {
   
	  for (int i = 0; i < 5; i++) {
   
	    for (long k = 0; k < 100000000; k++) ;
	    System.out.println(threadName + ": " + i);
	  } 
	}
  //④在实现类中重写Runnable接口的start()方法,并分配线程
  public void start () {
   
		System.out.println("启动 " +  threadName );
		if (t == null) {
   
			t = new Thread (this, threadName);
			t.start ();
		}
	}
}

/*
测试程序实现的类
**/

//④创建RunnableDemo实现的测试类ThreadTest
public class ThreadTest{
   
	public static void main(String args[]) {
   
		//⑤实例化RunnableDemo对象
		RunnableDemo R1 = new RunnableDemo( "Thread_1");
		RunnableDemo R2 = new RunnableDemo( "Thread_2");
		//实例化Thread子类
		Thread t1 = new Thread(R1);
		Thread t2 = new Thread(R2);
		//⑥调用重写的start()方法,启动线程
		System.out.println("线程开始执行之前,线程isAlive?-->"+t1.isAlive());  //使用IsAlive()方法查看线程是否活着
		t1.start();
		System.out.println("线程开始执行之后,线程isAlive?-->"+t1.isAlive());
		t2.start();
	}
}
Console:

创建Thread_1
创建Thread_2
线程开始执行之前,线程isAlive?-->false
线程开始执行之后,线程isAlive?-->true
Thread_1: 0
Thread_2: 0
Thread_2: 1
Thread_1: 1
Thread_2: 2
Thread_1: 2
Thread_2: 3
Thread_1: 3
Thread_2: 4
Thread_1: 4

⚠️注意:主线程有可能比其他线程先执行完。

3、线程休眠

能控制线行为的方法之一就是调用sleep()方法让线程休眠,线程休眠指的是让线程暂缓执行,等到预计时间之后再恢复执行。

(1)线程休眠的操作方法

sleep()方法可以指定线程休眠时间,线程休眠的时间以毫秒为单位。

.sleep()
try{
   
  Thread.sleep(2000);
}catch(InterruptedException e){
   
  e.printStackTrace();
}

上述代码会使线程休眠2秒,从而在2秒内不进入到就绪状态。由于sleep()方法可能会抛出InterruptedException异常,所以将sleep()方法放在try…catch异常捕捉代码块中。虽然使用了sleep()方法的线程在一段时间后会醒来,但是并不能保证线程醒来后就能直接进入到运行状态,只能保证醒来的线程进入到就绪状态。

(2)线程休眠的逻辑

sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。执行逻辑可以总结为以下三步:

  • 线程休眠会交出CPU,让CPU去执行其他的任务。

  • 调用sleep()方法让线程进入休眠状态后,sleep()方法并不会释放锁,即当前线程持有某个对象锁时,即使调用sleep()方法其他线程也无法访问这个对象。

  • 调用sleep()方法让线程从运行状态转换为阻塞状态;sleep()方法调用结束后,线程从阻塞状态转换为可执行状态。

(3)线程休眠的实现

实例2:线程执行次数是3的倍数时,线程休眠2000毫秒

创建线程,重写run()方法,使2个线程循环执行,当每个线程执行次数是3的倍数时,线程休眠2000毫秒。

class ThreadSleep extends Thread{
   
  public ThreadSleep(String name){
   
    super(name);
  }
  public synchronized void run(){
   
    try{
   
      for(int i = 0 ; i<5 ; i++){
     
        //为3的倍数时,休眠2000毫秒
        if(i%3 == 0){
    
          Thread.sleep
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值