线程的生命周期及五种基本状态

导读:本篇文章讲解 线程的生命周期及五种基本状态,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

线程的创建

1. 继承Thread类

  • 特点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
public class TestDemo extends Thread{
    @Override
    public void run() {
       //线程会执行的指令
        System.out.println("Come in");
    }
    public static void main(String[] args) {
        TestDemo testDemo=new TestDemo();
        testDemo.start();
    }
}

2. 实现Runnable接口

  • 特点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
public class TestDemo implements Runnable{
    @Override
    public void run() {
        System.out.println("Come in");
    }

    public static void main(String[] args) {
        Thread t=new Thread(new TestDemo());
        t.start();
        System.out.println("end ");
    }
}

3. 使用Callable和Feature

  • 特点:带有返回值
public class CallableDemo implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("come in");
        return "SUCCESS";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableDemo callableDemo=new CallableDemo();
        FutureTask<String> f=new FutureTask<String>(callableDemo);
        new Thread(f).start();
        System.out.println("返回值:"+f.get());
    }
}

线程的生命周期状态

在java语言中,一个线程从其创建、启动到其运行结束的整个生命周期可能经历若干状态,如下图所示:
在这里插入图片描述

  • new:一个已经创建而未启动的线程处于该状态。由于一个线程实例只能够被启动一次,因此一个线程只可能有一次处于该状态
  • runnnable:该状态可以被看成一个复合状态。它包括两个子状态:ready和running,前者表示处于该状态的线程可以被线程调度器(Schedule)进行调度而使之处于running状态。后者表示该状态的线程正在运行,即相应线程对象的run方法所对应的指令正在处理器中执行。执行Thread.yield()方法的线程,其状态可能会由running转换成ready。处于ready子状态的线程也被称为活跃线程。
  • blocked:一个线程发起一个阻塞式I/O操作后,或者申请一个由其他线程持有的独占资源(比如锁)时。相应的线程会处于该状态。处于blocked状态的线程不会占用处理器的资源。当阻塞式I/O操作完成后,或者线程获得了其申请的资源后,该线程的状态又可以转换成runnable。
  • waiting:一个线程执行了某些特定方法之后会处于这种等待其他线程执行另外一些特定操作的状态。能够使其执行线程变更为waiting状态的方法包括:Object.wait()、Object.join()和LockSupport.park(Object)。能够使相应线程从waiting变成runnable的相应方法包括:Object.notify/notifyAll()和LockSupport.unpark(Object)。
  • timed_waiting:还状态与waiting类似,差别在于处于该状态的线程并非无限制地等待其他线程执行特定操作,而是处于带有时间限制的等待状态。当其他线程没有在指定时间内执行该线程所期望的特定操作时,该线程的状态自动装换成runnable。
  • terminated:已经执行结束的线程处于该状态。由于一个线程实例只能够被启动一次,因此一个线程也只可能有一次处于该状态。Thread.run()正常返回或者由于抛出异常而提前终止都会导致相应的线程处于该状态。

一个线程在其整个生命周期中,只可能有一次处于new状态和terminated状态。

线程的终止

某些情况下,我们需要主动停止线程而不是等待线程自然终止(run方法返回),比如:

  • 服务或者系统关闭:当一个服务不在需要的时候,我们应该及时停止该服务所启动的工作者线程以节约宝贵的线程资源。
  • 错误处理:当一个线程出现不可恢复的异常时,其他线程往往就没有继续运行下去的必要,此时需要我们主动停止其他工作线程。
  • 用户取消任务:在某些耗时的任务执行过程中用户可能会取消这个任务,这时任务的取消往往是通过主动停止相应的工作者线程实现的。

然而,停止线程确是目标简单但实现并不是那么简单的一件事情:Java标准库并没有提供可以直接停止线程的 API 1
我们不难想到主动停止一个线程的实现思路:给待停止的线程设置一个线程停止标记(布尔型数据),目标线程检测到该标志值变为true时则设法让其run方法返回,这样就实现了线程的终止。
而在Java的标准库中,正好提供了这样的一些方法:

  • workerThread.interrupt():但是interrupt()并不是一个终止的方法,而是给工作线程发送中断标记的方法。同时,Java平台为每个线程维护了一个中断标记的布尔型状态变量用于表示是否接受到了中断,中断标记值为true表示相应线程收到了中断。
  • Thread.currentThread().isInterrupted():目标线程可以通过调用来获取线程的中断标记值
  • Thread.interrupted():获取并重置(也称清空)中断标记值,即Thread.interrupted()会返回当前线程的中断标记值并将当前线程中断标记重置为false。调用某一个线程的interrupted()相当于将该线程(目标线程)的中断标记置为true。
public class InterruptDemo implements Runnable{

    private int i=1;
    @Override
    public void run() {
//        Thread.currentThread().isInterrupted()=false;
//        表示一个中断的标记  interrupted=fasle
        while(!Thread.currentThread().isInterrupted()){
            System.out.println("Test:"+i++);
        }
    }

    public static void main(String[] args) {
        Thread thread=new Thread(new InterruptDemo());
        thread.start();
        thread.interrupt(); //设置 interrupted=true;
    }
}

还有一种情况:Java标准库中许多的阻塞方法对中断的响应方式都是抛出InterruptException等异常,包括Object.wait()、Thread.sleep()、Thread.join()等方法。当中断标记值为true则抛出InterruptException,同时,将中断标记值复位,即中断标记值变为false。
因此,Java应用层代码通常可以通过对InterruptException等异常进行处理的方式来实现中断响应。对InterruptException异常的正确处理方式包括:

  • 不捕获InterruptException:我们可以选择在这个方法的异常声明(throws)中也加一个InterruptException。即将这个异常抛出去,让其上层代码去解决。
  • 捕获InterruptException后重新将异常抛出:这种策略主要由于应用代码需要捕获InterruptException并对此做一些中间处理,最后再将异常抛给上层代码。
  • 捕获InterruptException并在捕获该异常后中断当前线程。这种策略实际上在捕获InterruptException后又恢复中断标志,即中断标记置为true。
public class InterruptDemo02 implements Runnable{
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){ //false
            try {
                TimeUnit.SECONDS.sleep(200);
            } catch (InterruptedException e) {//触发线程复位,变为false
                e.printStackTrace();
                //可以不做处理,则线程是运行状态
            }
        }
        System.out.println("processor End");
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new InterruptDemo02());
        t1.start();
        Thread.sleep(1000);
        t1.interrupt(); //有作用 true
    }
}

运行结果:
在这里插入图片描述
虽然抛出异常,但线程继续在运行

public class InterruptDemo02 implements Runnable{
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){ //false
            try {
                TimeUnit.SECONDS.sleep(200);
            } catch (InterruptedException e) {//触发线程复位,变为false
                e.printStackTrace();
                //继续中断 ->
               Thread.currentThread().interrupt(); //再次中断
            }
        }
        System.out.println("processor End");
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new InterruptDemo02());
        t1.start();
        Thread.sleep(1000);
        t1.interrupt(); //有作用 true
    }
}

运行结果:
在这里插入图片描述
线程停止运行


  1. Thread.stop()早已是废弃的方法了 ↩︎

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/12929.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!