前言

在讲线程之前有必要讨论一下进程的定义:进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。进程实体由程序段, 数据段 PCB(进程控制块)组成。线程又是什么?线程可以看做轻量级进程,线程是进程的执行单元,是进程调度的基本单位

本文将详细介绍关于Java线程状态及切换、关闭线程的相关内容,下面话不多说了,来一起看看详细的介绍吧

1、线程状态及切换

Java中的线程有六种状态,使用线程Thread内的枚举类来实现,如下,我对每个状态都进行了一定的解释。

public enum State {

/** 表示一个线程还没启用(即未调用start方法)*/

NEW,

/**

* JVM中执行的线程都是处于这个状态的,但是处于这个状态不一定在JVM中执行,

* 也就是说,只有这个状态有资格被JVM调度从而获得时间片执行。

*/

RUNNABLE,

/**

* 线程在等待获取锁资源从而进入阻塞状态,

* 在这个状态中,其一直监视锁的动态,随时准备抢占锁

* 若获得锁资源,重新进入RUNNABLE状态

*/

BLOCKED,

/**

* 当调用Object.wait、Thread.join或者LockSupport类的park方法的时候,线程进入此状态,

* 该状态若无其他线程主动唤醒,则无期限的等待。

* 唤醒的方法包括:Object.notify(唤醒随机一个)、Object.notifyAll(唤醒全部线程),

* 被唤醒的线程重新进入RUNNABLE状态

*/

WAITING,

/**

* 同WAITING状态,不过不同的是调用的方法加上了时间的限制,

* 例如:Object.wait(10)、Thread.sleep(10)、Thread.join(10)、LockSupport.parkNanos(10)、LockSupport.parkUntil(10)这些方法

* 唤醒的方法有两种:

* 1、时间过期。

* 2、其他线程调用了notify或者notifyAll

* 唤醒之后同样进入RUNNABLE状态

*/

TIMED_WAITING,

/** 线程的终点(正常死亡或者被终止)*/

TERMINATED;

}

除了NEW和TERMINATED之外,其他的状态都是可以相互转换的,其转换过程如下图所示

这里特别讲一下RUNNABLE状态,在这个状态中线程并不一定在执行程序,只有被JVM调度的线程才能获得执行的时间片,并且只有这个状态的线程才能够获得时间片,换句话说,被JVM调度并且获得时间片是只属于处于RUNNABLE状态线程的权利。为了便于理解,可以将RUNNABLE分成Runnable和Running两个状态(当然,你也可以换成其他的,这里我只是自己好理解),那么上面的线程转换图就转变成了下面这样(参考《Java并发编程的艺术》中的线程状态图):

关于线程状态转换的例子,可以通过下面的代码加深理解

public class Test {

public static void main(String[] args) {

Test test = new Test();

// 1.NEW状态

Thread thread = new Thread(() -> {

// 3.进行test对象锁的争夺,若抢到锁则继续执行,否则进入BLOCKED状态监控该锁,重新获得后进入RUNNABLE

synchronized (test) {

try {

// 4.进入TIMED_WAITING状态,100ms后重新进入RUNNABLE状态争夺时间片

Thread.sleep(100);

// 5.重新获得时间片之后,进入WAITING状态

test.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

// 6.正常run()方法执行完毕后线程结束,进入TERMINATED

});

// 2.调用start()方法,线程进入RUNNABLE状态

thread.start();

}

}

注:代码执行的顺序为注释的序号

2、正确的结束一个线程

在上面的例子中我们看到线程的run方法正常执行完毕之后线程就正常死亡进入TERMINATED状态了,那么如果我们有中途停止线程的需求,我们应该如何正确的结束一个线程呢?

使用interrupt()方法:在线程内部,其定义了一个变量来标识当前线程是否处于被打断状态,调用interrupt()方法则使这个状态变为true。我们采用这个方法加异常处理的方式来结束一个线程。

public static void main(String[] args) {

Thread thread = new Thread(() -> {

try {

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

// 这里的return是必须的,原因后面说明

return;

}

System.err.println("thread interrupt test...");

});

thread.start();

thread.interrupt();

System.out.println("main thread end...");

}

// 结果图:异常后面的语句不会打印

这里关于线程中的打断标识变量(之后以interrupt称)需要说明的是,在特定的情况下其状态会被重置。

1、线程内部在catch了异常了之后interrupt的状态会被重置为false。

2、线程调用了Thread.interrupted()方法之后,interrupt的状态会被重置为false。如果需要判断线程是否中断的话可以使用对象方法isInterrupted(),此方法不会重置。

所以在刚才的代码中需要加入return来结束线程,否则的话线程还是会继续往下执行,如下图

使用isInterrupted()实现:

public static void main(String[] args) throws InterruptedException {

Thread thread = new Thread(() -> {

while (!Thread.currentThread().isInterrupted()) {

int k = 0;

while (k++ < 10) {

System.out.println("do something..." + k);

}

}

System.err.println("thread end...");

});

thread.start();

Thread.sleep(1);

// 主线程流程执行完了,需要停止线程

thread.interrupt();

}

使用标识位来实现:定义一个变量标识线程是否终止,若终止了则退出run方法。跟上面isInterrupted()的实现一样,不过换成了volatile变量而已。

public class Test {

public static volatile boolean interrupted = false;

public static void main(String[] args) throws InterruptedException {

Thread thread = new Thread(() -> {

while (!interrupted) {

int k = 0;

while (k++ < 10) {

if (interrupted) {

System.err.println("thread invoke end....");

return;

}

System.out.println("do something..." + k);

}

}

System.err.println("thread end...");

});

thread.start();

Thread.sleep(1);

// 主线程流程执行完了,需要停止线程

interrupted = true;

}

}

// 结果图

stop()方法——不正确的线程中断方法

在线程提供的方法中还有一个方法可以强制关闭线程——stop()。这个方法可以说是相当的霸道,给人一种“我不管,我就是要你现在立刻死亡(指线程)”的感觉,并且其还会释放线程所有的锁资源,这样可能会导致出现数据不一致从而出现线程不安全的情况,如下面例子。

public class Test {

public static volatile boolean flag = false;

public int state = 0;

public static void main(String[] args) throws InterruptedException {

Test test = new Test();

Thread thread = new Thread(() -> {

synchronized (test) {

try {

test.state = 1;

Thread.sleep(100);

if (flag) {

test.state = 2;

}

System.err.println("thread execute finished...");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

thread.start();

Thread.sleep(1);

thread.stop();

flag = true;

System.out.println("state状态:" + test.state);

}

}

// 在这段代码中,进入线程时默认将state赋为1,接着过一段时间后如果触发了特定条件则把state赋为2,但是在特定条件触发之前,线程就被终止掉了,这个特定条件虽然符合但却没办法执行,从而导致数据的不一致。

// 结果图

所以,我们应该采用上面两种正确的方式而不是stop()来中止线程。此外,stop()方法若在线程start()之前执行,那么在线程启动的时候就会立即死亡。

若有不对之处,望各位不吝指教(反正免费,对吧)。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

java 切换主线程_Java线程状态及切换、关闭线程的正确姿势分享相关推荐

  1. java线程6种状态转换,java6种线程状态

    网上好多帖子把操作系统的线程状态跟java的线程状态混为一谈,导致误导了好多人: 现在根据java的Thread类来分析java的线程状态; java.lang.Thread内部枚举State pub ...

  2. java 1000个线程_java,一个程序建立1000个线程,每一个线程加1到一个变量sum。

    1.程序建立1000个线程,有可能造成系统创建大量线程而导致消耗完系统内存,还会增加创建和销毁线程上所花的时间以及系统资源的开销 2.在创建线程数多的情况下,可以考虑使用线程池 以下是Java自带的几 ...

  3. pthread_create函数阻塞了主线程_5个状态,Python 中线程的生命周期

    编 程 的 朝 圣 之 路 ---------------------------------------- 当程序中包含多个线程时,CPU 不是一直被特定的线程霸占,而是轮流执行各个线程. 那么,C ...

  4. java多线程交替打印_java多线程 更优雅的实现线程同步:交替打印A、B LockSupport实现...

    一 问题概述 线程或者进程之间有两种关系 同步和互斥,我们通常实现同步方法是使用线程的等待唤醒机制,而等待唤醒机制的使用是建立在互斥的继承上的.但是同步线程并不一定是必须要实现互斥的.比如一个线程打印 ...

  5. terminated 线程_Java【多线程系列】JUC线程池—2. 原理(二)、Callable和Future

    在"Java多线程系列--"基础篇"01之 基本概念"中,我们介绍过,线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态.线程池也有5种状态:然而 ...

  6. ThreadPoolExecutor线程池,shutdown和shutdownNow关闭线程池方式对比,以及确保线程池能够彻底关闭的一种方式

    1. ThreadPoolExecutor线程池 1.1 创建线程池,构造方法的几个参数说明及创建如下. 1.2 shutdown方式关闭线程池 a. 空闲且能interrupt表示该线程处于阻塞等待 ...

  7. java怎么看具体被挂起的线程_Java知多少(65)线程的挂起、恢复和终止

    有时,线程的挂起是很有用的.例如,一个独立的线程可以用来显示当日的时间.如果用户不希望用时钟,线程被挂起.在任何情形下,挂起线程是很简单的,一旦挂起,重新启动线程也是一件简单的事. 挂起,终止和恢复线 ...

  8. java暂停的方法_Java使用join方法暂停当前线程

    目标线程的join方法暂停当前线程,直到目前线程完成(从run()方法返回),供大家参考,具体内容如下 Java代码: package Threads; import java.io.IOExcept ...

  9. java 主线程等待_Java实现主线程等待子线程

    本文介绍两种主线程等待子线程的实现方式,以5个子线程来说明: 1.使用Thread的join()方法,join()方法会阻塞主线程继续向下执行. 2.使用Java.util.concurrent中的C ...

最新文章

  1. php 计算字符串相邻最大重复数_php如何解决字符串中重复字符的次数并且排序输出的方法...
  2. ubuntu 安装 talib
  3. 阿里巴巴国际站询盘是什么意思?如何提高询盘率
  4. Linux vim编写编译运行一个.c文件(centeos 8 HelloWorld.c)
  5. html 图片加速,35种加速网站访问的最好做法
  6. ps右键不显示编辑选项_PS基础教程|视图操作
  7. 一个好习惯可以让自己享受一辈子
  8. [2018.07.24 T3] 大原题
  9. 解决Android studio在虚拟机上可以正常运行,而在手机上闪退的问题
  10. PASCAL程序设计语言 PDF 分享
  11. CAUSALITY FOR MACHINE LEARNING
  12. 零成本学arduino教程——光敏电阻传感器
  13. Frosty the snowman(圣诞英文歌曲)铃声 Frosty the snowman...
  14. linux shell 发邮件
  15. 云呐|动环监控设备维护与常见故障处理
  16. CDA Level Ⅲ 模拟题(二)
  17. spss专题3:结果文件的基本操作(转载)
  18. week2-二分和二分搜索
  19. Idea中GsonFormat插件安装
  20. win10 乳白色风格_乳白奥运

热门文章

  1. 小程序 模糊查询_[轻应用小程序]如何使用信息查询功能?
  2. 机器学习算法优缺点对比及选择
  3. java怎么调用7zip进行压缩_JAVA使用7-zip解压缩带密码的Zip文件(非Proccess方法)...
  4. 3点 刚体运动 opencv_模态法动力学分析中的刚体模态
  5. mysql错误日志为aborting_MySQL 错误日志(Error Log)
  6. 屠榜CV还不是这篇论文的终极目标,它更大的目标其实是……
  7. Tensorflow2.0 tf.function和AutoGraph模式
  8. DGL_图的创建、保存、加载
  9. 【HTML/CSS】margin塌陷和合并问题
  10. 论文学习16-Going out on a limb: without Dependency Trees(联合实体关系抽取2017)