Java中的线程生命周期核心概念
作者:?sofia?? 发布时间:2021-10-14 13:37:54
前言:
在本文中,我们将详细讨论Java中的一个核心概念——线程的生命周期。我们将使用一个快速的图解,当然还有实用的代码片段来更好地理解线程执行期间的这些状态。
Java多线程
在Java语言中,多线程是由线程的核心概念驱动的。线程在其生命周期中会经历各种状态:
Java中线程的生命周期
java.lang.Thread
类包含一个静态枚举,它定义了它的潜在状态。在任何给定的时间点内,线程只能处于以下状态之一:
NEW – 一个新创建的线程,尚未开始执行
RUNNABLE – 正在运行或准备执行,但它正在等待资源分配
BLOCKED – 等待获取监视器锁以进入或重新进入同步块/方法
WAITING – 等待其他线程执行特定操作,无任何时间限制
TIMED_WAITING – 等待其他线程在指定时间段内执行特定操作
TERMINATED – 已完成执行
上图涵盖了所有这些状态;现在让我们详细讨论其中的每一项。
NEW
新线程(或出生线程)是已创建但尚未启动的线程。在我们使用start()
方法启动它之前,它一直保持此状态。
以下代码段显示了新创建的处于新状态的线程:
Runnable runnable = new NewState();
Thread t = new Thread(runnable);
Log.info(t.getState());
由于我们尚未启动上述线程,因此方法t.getState()
会打印:
NEW
Runnable
当我们创建了一个新线程并对其调用start()
方法时,它将从NEW
状态移动到RUNNABLE
状态。处于此状态的线程正在运行或准备运行,但它们正在等待来自系统的资源分配。
在多线程环境中,线程调度程序(JVM的一部分)为每个线程分配固定的时间量。因此,它会运行一段特定的时间,然后将控制权交给其他可运行的线程。
例如,让我们将t.start()
方法添加到前面的代码中,并尝试访问其当前状态:
Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
Log.info(t.getState());
此代码很可能返回以下输出:
RUNNABLE
请注意:在本例中,并不总是保证在控件到达t.getState()时,它仍处于可运行状态。
线程调度器可能会立即对其进行调度,并可能完成执行。在这种情况下,我们可能会得到不同的输出。
Blocked
当前没有资格运行的线程处于阻塞状态。它在等待监视器锁定并尝试访问被其他线程锁定的代码段时进入此状态。
让我们尝试重现这种状态:
public class BlockedState {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DemoThreadB());
Thread t2 = new Thread(new DemoThreadB());
t1.start();
t2.start();
Thread.sleep(1000);
Log.info(t2.getState());
System.exit(0);
}
}
class DemoThreadB implements Runnable {
@Override
public void run() {
commonResource();
}
public static synchronized void commonResource() {
while(true) {
// Infinite loop to mimic heavy processing
// 't1' won't leave this method
// when 't2' try to enter this
}
}
}
在此代码中:
我们创建了两个不同的线程—
t1
和t2
t1
启动并进入synchronized commonResource()
方法;这意味着只有一个线程可以访问它;在当前线程完成处理之前,将阻止尝试访问此方法的所有其他后续线程进一步执行当
t1
进入此方法时,它将保持在无限while
循环中;这只是为了模拟繁重的处理,以便所有其他线程都无法进入此方法现在,当我们启动t2时,它尝试进入
commonResource()
方法,t1
已经访问了该方法,因此t2
将保持在阻塞状态
处于这种状态,我们称之为t2.getState()
并获取输出,如下所示:
BLOCKED
Waiting
线程在等待其他线程执行特定操作时处于等待状态。
根据JavaDocs,任何线程都可以通过调用以下三种方法中的任何一种进入这种状态:
object.wait()
thread.join()
LockSupport.park()
请注意,在wait()
和join()
中,我们没有定义任何超时时间,因为下一节将介绍该场景。
现在,让我们尝试重现这种状态:
public class WaitingState implements Runnable {
public static Thread t1;
public static void main(String[] args) {
t1 = new Thread(new WaitingState());
t1.start();
}
public void run() {
Thread t2 = new Thread(new DemoThreadWS());
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
}
}
class DemoThreadWS implements Runnable {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
Log.info(WaitingState.t1.getState());
}
}
让我们讨论一下我们在这里做什么:
我们已经创建并启动了
t1
t1
创建t2
并启动它当
t2
的处理继续时,我们称之为t2.join()
,这会使t1
处于等待状态,直到t2
完成执行因为
t1
正在等待t2
完成,所以我们调用t1.getState()
来自t2
正如您所期望的那样,这里的输出是:
WAITING
Timed Waiting
当线程等待另一个线程在规定的时间内执行特定操作时,该线程处于TIMED_WAITING
状态。
根据JavaDocs,有五种方法可以将线程置于TIMED_WAITING
状态:
thread.sleep(long millis)
wait(int timeout)
或wait(int timeout, int nanos)
thread.join(long millis)
LockSupport.parkNanos
LockSupport.parkUntil
现在,让我们尝试快速重现这种状态:
public class TimedWaitingState {
public static void main(String[] args) throws InterruptedException {
DemoThread obj1 = new DemoThread();
Thread t1 = new Thread(obj1);
t1.start();
// The following sleep will give enough time for ThreadScheduler
// to start processing of thread t1
Thread.sleep(1000);
Log.info(t1.getState());
}
}
class DemoThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
}
}
这里,我们创建并启动了一个线程t1
,该线程进入睡眠状态,超时时间为5秒;
输出将为:
TIMED_WAITING
Terminated
这是死线程的状态。当它完成执行或异常终止时,它处于终止状态。
让我们在以下示例中尝试实现此状态:
public class TerminatedState implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TerminatedState());
t1.start();
// The following sleep method will give enough time for
// thread t1 to complete
Thread.sleep(1000);
Log.info(t1.getState());
}
@Override
public void run() {
// No processing in this block
}
}
在这里,虽然我们已经启动了线程t1
,但它是下一个语句Thread.sleep(1000)
为t1提供了足够的时间来完成,因此该程序为我们提供如下输出:
TERMINATED
除了线程状态之外,我们还可以检查isAlive()
方法以确定线程是否处于活动状态。例如,如果我们在此线程上调用isAlive()
方法:
Assert.assertFalse(t1.isAlive());
结论
在本文中,我们学习了Java中线程的生命周期。我们查看了线程定义的所有六个状态。陈述enum并用快速示例再现它们。虽然代码片段在几乎每台机器上都会给出相同的输出,但在某些例外情况下,我们可能会得到一些不同的输出,因为无法确定线程调度器的确切行为。
来源:https://javakk.com/2668.html


猜你喜欢
- 今天深度学习一下《Java并发编程的艺术》的第1章并发编程的挑战,深入理解Java多线程,看看多线程中的坑。注意,哈肯的程序员读书笔记并不是
- 代理模式代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问。主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远
- Pom依赖<parent> <groupId>org.springframework.bo
- 开发android的时候,尽管手机已经root但是DDMS中还是没有data/data路径怎么办?可以用cmd命令提示符为逐个文件夹设置权限
- 由于最近想要阅读下JDK1.8 中HashMap的具体实现,但是由于HashMap的实现中用到了红黑树,所以我觉得有必要先复习下红黑树的相关
- spring-cloud-config 配置中心实现Spring Cloud Config 用于为分布式系统中的基础设施和微服务应用提供集中
- ReferenceWhy using finalizers is a bad idea当在一个类中使用了另外一个实现了IDisposable
- 本文主要讲解利用android中Matrix控制图形的旋转缩放移动,具体参见一下代码:/** * 使用矩阵控制图片移动、缩放、旋
- 本文实例讲述了Java中缀表达式转后缀表达式实现方法。分享给大家供大家参考,具体如下:本文先给出思路与方法,最后将给出完整代码项目实战:ht
- 前言我们在日常的开发中有时候会遇到需要用到相机的需求,而相机也是很常用的东西,例如扫二维码啊拍照上传啊等等。这里我不讲像qq那样自定义很强的
- Android实现TextView超链接一共有五种方式:推荐第四种、第五种1. 直接在xml文件中配置autoLink属性(简单易用,效果单
- 本篇使用java自带的MessageDigest实现对文本的md5加密算法,具体代码如下: /** *@Description
- 1.常见字符串编码常见的字符串编码有:LATIN1 只能保存ASCII字符,又称ISO-8859-1。UTF-8 变长字节编码,一个字符需要
- GridView是类似于ListView的控件,只是GridView可以使用多个列来呈现内容,而ListView是以行为单位,所以用法上是差
- 本文实例为大家分享了java实现PDF转图片的具体代码,供大家参考,具体内容如下1.首先利用maven引入所需jar包<depende
- 想在Linux进行JAVA开发吗?环境如何搭建,第一个HelloWorld如何实现,下面马上奉献:1、环境搭建1.1 Java JDK 的安
- 一、获取apikey,appsecret与商户号注册公众号、商户号二、获取用户的OpenId1.设置【授权回调页面域名】官方解释:用户在网页
- 前言最近在使用Spring框架时遇到了一些问题,主要是Spring的事务传播问题,一个不带事务的方法调用带事务的方法,有时候会出现不回滚的情
- 引言第一眼看到这个题目,我相信大家都会脑子里面弹出来一个想法:这不都是 Spring 的注解么,加了这两个注解的类都会被最终封装成 Bean
- java提供了Comparable接口与Compatator接口,它们为数组或集合中的元素提供了排序逻辑,实现此接口的对象数组或集合可以通过