当前位置:首页 > 科技  > 软件

为什么有些人说JAVA线程五种状态,有些人说六种?

来源: 责编: 时间:2023-11-07 17:16:14 164观看
导读Java线程是Java并发编程的基础,理解Java线程的生命周期对于编写高效、稳定的并发程序至关重要。本文将从两个角度来介绍Java线程的生命周期,并通过代码示例进行验证。一、复习在Java中,线程的创建主要通过两种方式:继承Th

Java线程是Java并发编程的基础,理解Java线程的生命周期对于编写高效、稳定的并发程序至关重要。本文将从两个角度来介绍Java线程的生命周期,并通过代码示例进行验证。Idi28资讯网——每日最新资讯28at.com

一、复习

在Java中,线程的创建主要通过两种方式:继承Thread类或实现Runnable接口、Callnablee接口。以下是一个简单的示例:Idi28资讯网——每日最新资讯28at.com

1.1 创建线程的方式

1.1.1 创建建方式一:继承Thread类

步骤:Idi28资讯网——每日最新资讯28at.com

  1. 创建自定义类继承于Thread类,并重写Thread类的run()方法。该run()方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
  2. 创建自定义类(Thread子类)的实例,即创建了线程对象。
  3. 调用线程对象的start()方法来启动该线程。
/** * Java中创建线程方式一:继承Thread类 */public class ThreadTest extends Thread{    @Override    public void run() {        for (int i = 0; i < 10; i++) {            System.out.println(i);        }    }    public static void main(String[] args) {        ThreadTest threadTest = new ThreadTest();        threadTest.start();    }}

打印结果:Idi28资讯网——每日最新资讯28at.com

0123456789

1.1.2 创建方式二:实现Runnable接口

步骤:Idi28资讯网——每日最新资讯28at.com

  1. 创建自定义类实现于Runnable接口,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  2. 创建自定义类(Runnable实现类)的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  3. 调用线程对象的start()方法来启动该线程。
/** * Java中创建线程方式二:实现Runnable接口 */public class RunnableTest implements Runnable{    @Override    public void run() {        for (int i = 100; i < 110; i++) {            System.out.println(i);        }    }    public static void main(String[] args) {        RunnableTest runnableTest = new RunnableTest();        Thread thread = new Thread(runnableTest);        thread.start();    }}

打印结果:Idi28资讯网——每日最新资讯28at.com

100101102103104105106107108109

1.1.3 创建方式三:通过Callable和Future创建线程

Callable和Future出现的背景

一般创建线程时,使用上面两种方式居多。但是这两种方式都有一个缺陷:在执行完任务之后无法获取执行结果。Idi28资讯网——每日最新资讯28at.com

如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。Idi28资讯网——每日最新资讯28at.com

而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。Idi28资讯网——每日最新资讯28at.com

Callable和Future简介

Callable接口可以理解成一段可以调用并返回结果的代码(call方法);Idi28资讯网——每日最新资讯28at.com

Future接口表示异步任务,是还没有完成的任务给出的未来结果。Idi28资讯网——每日最新资讯28at.com

所以说Callable用于产生结果,Future用于获取结果。这点可以在源码里面分析得知。Idi28资讯网——每日最新资讯28at.com

源码分析Idi28资讯网——每日最新资讯28at.com

先看Runnable源码

Runnable位于java.lang包下,它是一个接口,在它里面声明了一个方法叫做 run():Idi28资讯网——每日最新资讯28at.com

@FunctionalInterfacepublic interface Runnable {    public abstract void run();}

由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。Idi28资讯网——每日最新资讯28at.com

再看Callable源码Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():Idi28资讯网——每日最新资讯28at.com

@FunctionalInterfacepublic interface Callable<V> {    V call() throws Exception;}

可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。Idi28资讯网——每日最新资讯28at.com

Future源码

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。为什么这么说呢?看了它的源码就知道了。Idi28资讯网——每日最新资讯28at.com

Future类位于java.util.concurrent包下,它也是一个接口Idi28资讯网——每日最新资讯28at.com

package java.util.concurrent;public interface Future<V> {	/**     * 取消任务     */    boolean cancel(boolean mayInterruptIfRunning);	/**     * 任务是否被取消成功     */    boolean isCancelled();	/**     * 任务是否已经完成     */    boolean isDone();		/**     * 获取执行结果     */    V get() throws InterruptedException, ExecutionException;	/**     * 获取执行结果,支持超时     */    V get(long timeout, TimeUnit unit)        throws InterruptedException, ExecutionException, TimeoutException;}

所以说Future一共给我们提供了三种功能:Idi28资讯网——每日最新资讯28at.com

  • 能够取消任务。
  • 判断任务是否完成。
  • 能够获取任务执行结果。

但是因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。Idi28资讯网——每日最新资讯28at.com

FutureTask实现于RunnableFuture接口,这个接口的定义如下:Idi28资讯网——每日最新资讯28at.com

public interface RunnableFuture<V> extends Runnable, Future<V> {    void run();}

可以看到这个接口实现了Runnable和Future接口,接口中的具体实现由FutureTask来实现。这个类的两个构造方法如下 :Idi28资讯网——每日最新资讯28at.com

public FutureTask(Callable<V> callable) {        if (callable == null)            throw new NullPointerException();        this.callable = callable;        this.state = NEW;       // ensure visibility of callable    }	public FutureTask(Runnable runnable, V result) {        this.callable = Executors.callable(runnable, result);        this.state = NEW;       // ensure visibility of callable    }

如上提供了两个构造函数,一个以Callable为参数,另外一个以Runnable为参数。这些类之间的关联允许你基于FutureTask的Runnable特性(因为它实现了Runnable接口),把任务写成Callable,然后封装进一个由执行者调度并在必要时可以取消的FutureTask。Idi28资讯网——每日最新资讯28at.com

FutureTask可以由执行者调度,它对外提供的方法基本上就是Future和Runnable接口的组合:get()、cancel、isDone()、isCancelled()和run(),而run()方法通常都是由执行者调用,我们基本上不需要直接调用它。Idi28资讯网——每日最新资讯28at.com

通过Callable和Future创建一个线程

步骤:Idi28资讯网——每日最新资讯28at.com

  1. 创建自定义类实现于Callable接口,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
  2. 创建自定义类(Callable实现类)的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
  3. 使用FutureTask对象作为Thread对象的target创建并启动新线程。
  4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

示例:Idi28资讯网——每日最新资讯28at.com

/** * Java中创建线程方式三:Callable和FutureTask结合使用 */public class CallableTest implements Callable{    @Override    public Object call() throws Exception {        int i = 1000;        for ( ; i < 1010; i++) {            System.out.println(i);        }        return 1111;    }    public static void main(String[] args) {        CallableTest callableTest = new CallableTest();        FutureTask<Integer> futureTask = new FutureTask<Integer>(callableTest);        Thread thread = new Thread(futureTask);        thread.start();        try {            System.out.println("Result:"+futureTask.get());        } catch (InterruptedException | ExecutionException e) {            e.printStackTrace();        }    }}

打印结果Idi28资讯网——每日最新资讯28at.com

1000100110021003100410051006100710081009Result:1111

1.2 面试题:Runable接口和Calla

返回值:

  • Runnable接口的run()方法没有返回值,它表示一个没有返回结果的任务。
  • Callable接口的call()方法有返回值,可以返回计算结果。

异常处理:

  • Runnable接口的run()方法不能抛出受检查异常,只能通过捕获异常并在方法内部处理。
  • Callable接口的call()方法可以抛出受检查异常,调用者需要捕获并处理异常。

使用方式

  • Runnable接口通常用于执行没有返回结果的任务,可以通过Thread类的构造函数来创建线程并传递一个Runnable对象。
  • Callable接口通常用于执行有返回结果的任务,需要配合ExecutorService接口或Future接口来提交和执行任务。

返回结果获取

  • Runnable接口没有提供直接获取任务执行结果的方法。
  • Callable接口的call()方法返回一个Future对象,通过该对象可以获取任务的执行结果。ble接口的区别

二、线程池的生命周期

Java线程的状态可以被划分为五种或六种,这主要取决于你从哪个角度来看。在操作系统的传统线程模型中,线程通常被分为五种状态。Idi28资讯网——每日最新资讯28at.com

2.1 从JVM源代码看线程周期:

  1. 初始 (NEW) :新创建了一个线程对象,但还没有调用start ()方法3。
  2. 运行 (RUNNABLE) :Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
  3. 阻塞 (BLOCKED) :表示线程阻塞于锁3。
  4. 等待 (WAITING) :进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
  5. 超时等待 (TIMED_WAITING) :该状态不同于WAITING,它可以在指定的时间后自行返回
  6. 终止 (TERMINATED) :表示该线程已经执行完毕

Idi28资讯网——每日最新资讯28at.com

2.2 从操作系统的层面来看:

Idi28资讯网——每日最新资讯28at.com

  1. 新建状态 (New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread ()2。
  2. 就绪状态 (Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start ()方法,从而来启动该线程。例如,thread.start ()。处于就绪状态的线程,随时可能被CPU调度执行2。
  3. 运行状态 (Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态2。
  4. 阻塞状态 (Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:等待阻塞、同步阻塞和其他阻塞2。
  5. 死亡状态 (Dead): 线程执行完了或者因异常退出了run ()方法,该线程结束生命周期2。

总结:

操作系统层面的五种线程状态和JVM的六种线程状态是两个不同层次的概念,它们之间并不是一一对应的关系。Idi28资讯网——每日最新资讯28at.com

JVM并不关心操作系统线程的实际状态,从JVM看来,等待CPU使用权(操作系统状态为可运行态)与等待I/O(操作系统处于等待状态)没有区别,都是在等待某种资源,所以都归入RUNNABLE状态。因此,操作系统层面的线程状态并不直接影响JVM的线程状态。Idi28资讯网——每日最新资讯28at.com

这两者的主要区别在于它们关注的焦点不同:操作系统更关注线程对CPU和I/O资源的使用,而JVM更关注线程在Java程序中的行为。Idi28资讯网——每日最新资讯28at.com

在「JDK1.2之后」,Java线程模型已经确定了基于操作系统原生线程模型实现。因此,目前或者今后的JDK版本中,操作系统支持怎么样的线程模型,在很大程度上决定了Java虚拟机的线程如何映射,这一点在不同的平台上没有办法达成一致,虚拟机规范中也未限定Java线程需要使用哪种线程模型来实现。线程模型只对线程的并发规模和操作成本产生影响,对于Java程序来说,这些差异是透明的。Idi28资讯网——每日最新资讯28at.com

对应Oracle Sun JDK或者说Oracle Sun JVM而言,它的Windows版本和Linux版本都是使用「一对一的线程模型」实现的。Idi28资讯网——每日最新资讯28at.com

一对一的线程模型也就是一条Java线程就映射到一条轻量级进程(「Light Weight Process」)中,而一条轻量级线程又映射到一条内核线程(「Kernel-Level Thread」)。我们平时所说的线程,往往就是指轻量级进程(或者通俗来说我们平时新建的java.lang.Thread就是轻量级进程实例的一个"句柄",因为一个java.lang.Thread实例会对应JVM里面的一个JavaThread实例,而JVM里面的JavaThread就应该理解为轻量级进程)。推算这个线程映射关系,可以知道,我们在应用程序中创建或者操作的java.lang.Thread实例最终会映射到系统的内核线程,如果我们恶意或者实验性无限创建java.lang.Thread实例,最终会影响系统的正常运行甚至导致系统崩溃(可以在Windows开发环境中做实验,确保内存足够的情况下使用死循环创建和运行java.lang.Thread实例)。Idi28资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-17526-0.html为什么有些人说JAVA线程五种状态,有些人说六种?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 使用Java AOP实现面向切面编程

下一篇: 为什么 Kafka 的吞吐量那么高?

标签:
  • 热门焦点
  • 石头智能洗地机A10 Plus体验:双向自清洁治好了我的懒癌

    石头智能洗地机A10 Plus体验:双向自清洁治好了我的懒癌

    一、前言和介绍专为家庭请假懒人而生的石头科技在近日又带来了自己的全新旗舰新品,石头智能洗地机A10 Plus。从这个产品名上就不难看出,这次石头推出的并不是常见的扫地机器
  • Redmi Buds 4开箱简评:才199还有降噪 可以无脑入

    Redmi Buds 4开箱简评:才199还有降噪 可以无脑入

    在上个月举办的Redmi Note11T Pro系列新机发布会上,除了两款手机新品之外,Redmi还带来了两款TWS真无线蓝牙耳机产品,Redmi Buds 4和Redmi Buds 4 Pro,此前我们在Redmi Note11T
  • 5月iOS设备好评榜:iPhone 14仅排第43?

    5月iOS设备好评榜:iPhone 14仅排第43?

    来到新的一月,安兔兔的各个榜单又重新汇总了数据,像安卓阵营的榜单都有着比较大的变动,不过iOS由于设备的更新换代并没有那么快,所以相对来说变化并不大,特别是iOS好评榜,老款设
  • 一篇聊聊Go错误封装机制

    一篇聊聊Go错误封装机制

    %w 是用于错误包装(Error Wrapping)的格式化动词。它是用于 fmt.Errorf 和 fmt.Sprintf 函数中的一个特殊格式化动词,用于将一个错误(或其他可打印的值)包装在一个新的错误中。使
  • 一文搞定Java NIO,以及各种奇葩流

    一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 梁柱接棒两年,腾讯音乐闯出新路子

    梁柱接棒两年,腾讯音乐闯出新路子

    文丨田静 出品丨牛刀财经(niudaocaijing)7月5日,企鹅FM发布官方公告称由于业务调整,将于9月6日正式停止运营,这意味着腾讯音乐长音频业务走向消亡。腾讯在长音频领域还在摸索。为
  • 一条抖音4亿人围观 ! 这家MCN比无忧传媒还野

    一条抖音4亿人围观 ! 这家MCN比无忧传媒还野

    作者:Hiu 来源:互联网品牌官01 擦边少女空降热搜,幕后推手曝光被网友誉为&ldquo;纯欲天花板&rdquo;的女网红井川里予,近期因为一组哥特风照片登上热搜,引发了一场互联网世界关于
  • 品牌洞察丨服务本地,美团直播成效几何?

    品牌洞察丨服务本地,美团直播成效几何?

    来源:17PR7月11日,美团App首页推荐位出现&ldquo;美团直播&rdquo;的固定入口。在直播聚合页面,外卖&ldquo;神枪手&rdquo;直播间、美团旅行直播间、美团买菜直播间等均已上线,同时
  • Meta盲目扩张致超万人被裁,重金押注元宇宙而前景未明

    Meta盲目扩张致超万人被裁,重金押注元宇宙而前景未明

    图片来源:图虫创意日前,Meta创始人兼CEO 马克&middot;扎克伯发布公开信,宣布Meta计划裁员超11000人,占其员工总数13%。他公开承认了自己的预判失误:&ldquo;不仅
Top
Baidu
map