【面试精讲】Java线程6种状态和工作原理详解,Java创建线程的4种方式

这篇具有很好参考价值的文章主要介绍了【面试精讲】Java线程6种状态和工作原理详解,Java创建线程的4种方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Java线程6种状态和工作原理详解,Java创建线程的4种方式

目录

一、Java线程的六种状态

二、Java线程是如何工作的?

三、BLOCKED 和 WAITING 的区别

四、start() 和 run() 源码分析

五、Java创建线程的所有方式和代码详解

1. 继承Thread类

2. 实现Runnable接口

3. 实现Callable接口与FutureTask

4. 使用线程池

总结

 博主v:XiaoMing_Java


在并发编程领域,Java线程是实现多任务处理的基石。了解其状态及工作原理对于开发高效、稳定的Java应用至关重要。本文将深入探讨Java线程的各种状态以及它们的工作机制。

一、Java线程的六种状态

Java线程在其生命周期内可以处于以下几种状态:

  1. 新建(New): 线程被创建后,但在调用start()方法之前的状态。
  2. 可运行(Runnable): 线程已经启动,并且执行start()方法后的状态。在此状态,线程可能正在运行也可能正在等待CPU分配时间片,具体取决于操作系统的线程调度器。
  3. 阻塞(Blocked): 当一个线程试图获取一个对象的锁(不是通过synchronized块或方法),而该锁被其他线程持有,则该线程会进入阻塞状态。
  4. 等待(Waiting): 当一个线程等待另一个线程执行特定动作时(例如,通过Object.wait()Thread.join()LockSupport.park()),该线程会进入等待状态。
  5. 超时等待(Timed Waiting): 类似于等待状态,但有最大等待时间限制。当线程调用带有指定等待时间的sleep()wait()join(), 或LockSupport.parkNanos()/parkUntil()时,会处于该状态。
  6. 终止(Terminated): 线程的运行结束,无论是正常完成还是中途退出。
public enum State {
    /**
     * 新建状态,线程被创建出来,但尚未启动时的线程状态
     */
    NEW,

    /**
     * 就绪状态,表示可以运行的线程状态,但它在排队等待来自操作系统的 CPU 资源
     */
    RUNNABLE,

    /**
     * 阻塞等待锁的线程状态,表示正在处于阻塞状态的线程,正在等待监视器锁,比如等待执行 synchronized 代码块或者
     * 使用 synchronized 标记的方法
     */
    BLOCKED,

    /**
     * 等待状态,一个处于等待状态的线程正在等待另一个线程执行某个特定的动作。
     * 例如,一个线程调用了 Object.wait() 它在等待另一个线程调用
     * Object.notify() 或 Object.notifyAll()
     */
    WAITING,

    /**
     * 计时等待状态,和等待状态 (WAITING) 类似,只是多了超时时间,比如
     * 调用了有超时时间设置的方法 Object.wait(long timeout) 和 
     * Thread.join(long timeout) 就会进入此状态
     */
    TIMED_WAITING,

    /**
     * 终止状态,表示线程已经执行完成
     */
}

二、Java线程是如何工作的?

Java线程的工作机制与操作系统的线程管理紧密相关。Java虚拟机(JVM)通过映射到底层操作系统的原生线程实现来管理Java线程。这个过程主要包括线程的创建、调度、上下文切换、以及终止等方面。

  • 创建: 当在Java程序中创建线程对象并调用其start()方法时,JVM会向操作系统请求创建一个新的线程。操作系统为此分配资源并开始执行线程的run()方法。
  • 调度与执行: 一旦线程处于可运行状态,它就成为了操作系统调度器的候选对象。基于操作系统的策略和当前情况(如优先级、CPU亲和性等),调度器选择线程并分配CPU时间片进行执行。在任意给定时刻,单核CPU只能执行一个线程,而多核CPU可以并行执行多个线程。
  • 上下文切换: 当当前执行的线程由于时间片耗尽、等待IO操作、试图获取锁等原因需要被暂停时,操作系统保存当前线程的状态(称为“上下文”),并恢复另一个线程的上下文以继续执行。这个保存和恢复的过程称为上下文切换。
  • 等待与通知: 当线程进入等待状态时,它会释放所有持有的锁,直到其他线程通过特定的通知机制(如notify()notifyAll()方法)将其唤醒。此时,线程再次竞争获取必要的锁以继续执行。
  • 终止: 线程执行完毕后,或者因为未捕获的异常而终止,JVM会清理线程所占用的所有资源,并将线程标记为终止状态。

线程的工作模式是,首先先要创建线程并指定线程需要执行的业务方法,然后再调用线程的 start() 方法,此时线程就从 NEW(新建)状态变成了 RUNNABLE(就绪)状态,此时线程会判断要执行的方法中有没有 synchronized 同步代码块,如果有并且其他线程也在使用此锁,那么线程就会变为 BLOCKED(阻塞等待)状态,当其他线程使用完此锁之后,线程会继续执行剩余的方法。

当遇到 Object.wait() 或 Thread.join() 方法时,线程会变为 WAITING(等待状态)状态,如果是带了超时时间的等待方法,那么线程会进入 TIMED_WAITING(计时等待)状态,当有其他线程执行了 notify() 或 notifyAll() 方法之后,线程被唤醒继续执行剩余的业务方法,直到方法执行完成为止,此时整个线程的流程就执行完了,执行流程如下图所示:

【面试精讲】Java线程6种状态和工作原理详解,Java创建线程的4种方式,Java全栈白宝书,# Java核心技术,java,开发语言,后端,jvm

三、BLOCKED 和 WAITING 的区别

虽然 BLOCKED 和 WAITING 都有等待的含义,但二者有着本质的区别,首先它们状态形成的调用方法不同,其次 BLOCKED 可以理解为当前线程还处于活跃状态,只是在阻塞等待其他线程使用完某个锁资源;而 WAITING 则是因为自身调用了 Object.wait() 或着是 Thread.join() 又或者是 LockSupport.park() 而进入等待状态,只能等待其他线程执行某个特定的动作才能被继续唤醒,比如当线程因为调用了 Object.wait() 而进入 WAITING 状态之后,则需要等待另一个线程执行 Object.notify() 或 Object.notifyAll() 才能被唤醒。

四、start() 和 run() 源码分析

首先从 Thread 源码来看,start() 方法属于 Thread 自身的方法,并且使用了 synchronized 来保证线程安全

public synchronized void start() {
    // 状态验证,不等于 NEW 的状态会抛出异常
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    // 通知线程组,此线程即将启动
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            // 不处理任何异常,如果 start0 抛出异常,则它将被传递到调用堆栈上
        }
    }
}

run() 方法为 Runnable 的抽象方法,必须由调用类重写此方法,重写的 run() 方法其实就是此线程要执行的业务方法

public class Thread implements Runnable {
 // 忽略其他方法......
  private Runnable target;

  @Override
  public void run() {
      if (target != null) {
          target.run();
      }
  }
}

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

五、Java创建线程的所有方式和代码详解

Java提供了多种创建和管理线程的方式,从简单的Thread类继承到实现RunnableCallable接口,再到使用强大的线程池管理。

选择合适的方式取决于具体的应用场景和需求。对于简单的任务,直接使用Thread类或Runnable接口可能就足够了。而对于需要任务执行结果的情况,Callable接口将是更好的选择。

在处理大量并发任务时,利用线程池可以显著提高性能和资源利用率。了解和掌握这些方法,对于编写高效、稳定的Java多线程程序至关重要。

1. 继承Thread类

Java允许通过继承Thread类的方式创建线程。这种方式简单直观,适用于简单的线程任务。

class MyThread extends Thread {
    @Override
    public void run() {
        // 这里填写线程任务代码
        System.out.println("线程运行中...");
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start(); // 启动新线程
    }
}

2. 实现Runnable接口

实现Runnable接口是创建线程最常见的方式之一,它提供了更大的灵活性,允许线程类继承其他类。

class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程任务
        System.out.println("通过Runnable接口运行线程...");
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start(); // 启动线程
    }
}

3. 实现Callable接口与FutureTask

Callable接口类似于Runnable,但它可以返回执行结果,并且能抛出异常。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // 返回执行结果
        return 123;
    }
}

public class CallableExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
        Thread t = new Thread(futureTask);
        t.start(); // 启动线程

        // 获取执行结果
        Integer result = futureTask.get();
        System.out.println("Callable返回的结果: " + result);
    }
}

4. 使用线程池

Java的Executor框架提供了一个强大的线程池管理机制,能更高效地管理线程生命周期。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5); // 创建固定大小的线程池

        for (int i = 0; i < 10; i++) {
            Runnable worker = new MyRunnable();
            executor.execute(worker);  // 提交任务给线程池执行
        }
        
        executor.shutdown(); // 关闭线程池
        while (!executor.isTerminated()) {
            // 等待所有任务完成
        }
        
        System.out.println("所有线程已完成任务");
    }
}

总结

理解Java线程的状态及其转换是掌握Java并发编程的关键。正确地管理线程状态,合理利用同步机制,可以有效提高Java应用的性能和响应速度。探讨Java线程的六种状态、Java线程是如何工作的,Java创建线程【继承Thread类、实现Runnable接口、实现Callable接口与FutureTask、使用线程池】的所有方式

如果本文对你有帮助 欢迎 关注 、点赞 、收藏 、评论, 博主才有动力持续记录遇到的问题!!!

 博主v:XiaoMing_Java

  📫作者简介:嗨,大家好,我是 小明(小明Java问道之路),互联网大厂后端研发专家,2022博客之星TOP3 / 博客专家 / CSDN后端内容合伙人、InfoQ(极客时间)签约作者、阿里云签约博主、全网 6 万粉丝博主。


🍅 文末获取联系 🍅  👇🏻 精彩专栏推荐订阅收藏 👇🏻

专栏系列(点击解锁)

学习路线(点击解锁)

知识定位

🔥Redis从入门到精通与实战🔥

Redis从入门到精通与实战

围绕原理源码讲解Redis面试知识点与实战

🔥MySQL从入门到精通🔥

MySQL从入门到精通

全面讲解MySQL知识与企业级MySQL实战

🔥计算机底层原理🔥

深入理解计算机系统CSAPP

以深入理解计算机系统为基石,构件计算机体系和计算机思维

Linux内核源码解析

围绕Linux内核讲解计算机底层原理与并发

🔥数据结构与企业题库精讲🔥

数据结构与企业题库精讲

结合工作经验深入浅出,适合各层次,笔试面试算法题精讲

🔥互联网架构分析与实战🔥

企业系统架构分析实践与落地

行业最前沿视角,专注于技术架构升级路线、架构实践

互联网企业防资损实践

互联网金融公司的防资损方法论、代码与实践

🔥Java全栈白宝书🔥

精通Java8与函数式编程

本专栏以实战为基础,逐步深入Java8以及未来的编程模式

深入理解JVM

详细介绍内存区域、字节码、方法底层,类加载和GC等知识

深入理解高并发编程

深入Liunx内核、汇编、C++全方位理解并发编程

Spring源码分析

Spring核心七IOC/AOP等源码分析

MyBatis源码分析

MyBatis核心源码分析

Java核心技术

只讲Java核心技术文章来源地址https://www.toymoban.com/news/detail-839256.html

到了这里,关于【面试精讲】Java线程6种状态和工作原理详解,Java创建线程的4种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java八股文面试[多线程]——主内存和工作内存的关系

    JAVA内存模型(JMM) 共享变量 :如果一个变量在多个线程的工作内存中 都存在副本 ,那么这个变量就是这几个线程的共享变量。 上面的工作内存其实是java内存模型 抽象出来的概念 ,下面简要介绍一下java内存模型(JMM)。 java内存模型( java memory model ): 描述了java程序中各

    2024年02月10日
    浏览(44)
  • 面试官:Java 线程有哪几种状态?它们之间是怎么切换的?

    来源:https://blog.csdn.net/limenghua9112/article/details/106975105 线程是 JVM 执行任务的最小单元,理解线程的状态转换是理解后续多线程问题的基础。 在 JVM 运行中,线程一共有 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 六种状态,这些状态对应 Thread.State 枚举类中的状态。 推荐

    2024年02月12日
    浏览(49)
  • 六种Java线程状态及jstack命令详解

    新建状态(New) :当创建一个Thread实例后,线程就处于新建状态。此时线程对象已经被分配了内存,并初始化了其成员变量的值。 就绪状态(Runnable) :也被称为“可执行状态”。当调用了线程的start()方法后,线程就进入了就绪状态。此时线程已经具备了执行的条件,等待

    2024年03月18日
    浏览(42)
  • 【Java面试题】线程创建的三种方式及区别?

    继承Thread类,子类重写run()方法,调用子类的strat()启动线程。 实现Runnable接口,实现run()方法,调用对象start()启动线程。 实现Callable接口,实现call()方法,用FutureTask()封装实现类。使用FutureTask对象作为Thread对象调用start()启动线程,调用FutureTask对象的get()

    2024年02月12日
    浏览(49)
  • Hive创建分区表时提示错误,java面试线程池

    at org.apache.hadoop.hive.ql.parse.HiveParser.alterTableStatementSuffix(HiveParser.java:7971) at org.apache.hadoop.hive.ql.parse.HiveParser.alterStatement(HiveParser.java:7447) at org.apache.hadoop.hive.ql.parse.HiveParser.ddlStatement(HiveParser.java:4337) at org.apache.hadoop.hive.ql.parse.HiveParser.execStatement(HiveParser.java:2494) at org.apache.hadoop

    2024年04月10日
    浏览(52)
  • 面试必问的Java 线程池原理及最佳实践

    1. 概述 1.1 线程池是什么 线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。 创建线程本身开销大,反复创建并销毁,过多的占用内存。所以有大量线程创建考虑使用线程池 。线程池不用反复创建线程达到 线程的复用 ,更具配置

    2024年02月01日
    浏览(44)
  • java八股文面试[多线程]——Synchronized的底层实现原理

    笔试:画出Synchronized 线程状态流转 实现原理图 synchronized解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是 同步 ,也称之为”同步锁“。 synchronized的作用是保证在 同一时刻 , 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的

    2024年02月10日
    浏览(46)
  • java八股文面试[多线程]——ThreadLocal底层原理和使用场景

    源码分析: ThreadLocal中定义了ThreadLocalMap静态内部类,该内部类中又定义了Entry内部类。 ThreadLocalMap定了 Entry数组。 Set方法: Get方法: Thread中定义了两个ThreaLocalMap成员变量: Spring使用ThreadLocal解决线程安全问题  我们知道在一般情况下,只有 无状态的Bean 才可以在多线程环

    2024年02月10日
    浏览(49)
  • git文件管理与索引,深入理解工作原理,java面试手册升级版

    git add 命令的意义是将暂存一个文件。以Git文件分类而言,如果一个文件是未追踪的,那么 git add 会将文件的状态转化为 已追踪状态 。如果git add 作用一个目录 ,那么该目录下的 所有文件都会被递归为已追踪状态暂存起来 。接着之前的例子,继续进行讲解。 $ git status On b

    2024年04月12日
    浏览(45)
  • 【Java 基础篇】Java多线程编程详解:线程创建、同步、线程池与性能优化

    Java是一门强大的编程语言,其中最引人注目的特性之一是多线程支持。多线程允许我们在同一程序中同时执行多个任务,这大大提高了应用程序的性能和响应能力。本文将深入介绍Java线程的基础知识,无论您是初学者还是有一些经验的开发人员,都将从中获益。 在计算机科

    2024年02月07日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包