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线程在其生命周期内可以处于以下几种状态:
- 新建(New): 线程被创建后,但在调用
start()
方法之前的状态。 - 可运行(Runnable): 线程已经启动,并且执行
start()
方法后的状态。在此状态,线程可能正在运行也可能正在等待CPU分配时间片,具体取决于操作系统的线程调度器。 - 阻塞(Blocked): 当一个线程试图获取一个对象的锁(不是通过synchronized块或方法),而该锁被其他线程持有,则该线程会进入阻塞状态。
- 等待(Waiting): 当一个线程等待另一个线程执行特定动作时(例如,通过
Object.wait()
、Thread.join()
或LockSupport.park()
),该线程会进入等待状态。 - 超时等待(Timed Waiting): 类似于等待状态,但有最大等待时间限制。当线程调用带有指定等待时间的
sleep()
,wait()
,join()
, 或LockSupport.parkNanos()
/parkUntil()
时,会处于该状态。 - 终止(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() 方法之后,线程被唤醒继续执行剩余的业务方法,直到方法执行完成为止,此时整个线程的流程就执行完了,执行流程如下图所示:
三、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
类继承到实现Runnable
或Callable
接口,再到使用强大的线程池管理。
选择合适的方式取决于具体的应用场景和需求。对于简单的任务,直接使用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核心技术