Java并发编程中,其中一个难点是对线程生命周期的理解,和多种线程控制方法、线程沟通方法的灵活运用。这些方法和概念之间彼此联系紧密,共同构成了Java并发编程基石之一。

Java线程的生命周期

Java线程类定义了New、Runnable、Running Man、Blocked和Dead五种状态。

New

当初始化了一个线程对象之后,线程就进入了New的状态。此时JVM会为其分配堆内存、初始化成员变量的值,获取当前线程为父线程。

Runnable

当调用线程对象的start方法之后,就进入Runnable状态。JVM会为其创建虚拟机栈和程序计数器。此时仅表明线程可以开始运行,但何时运行取决于JVM的线程调度器。

Running

当线程获取到CPU资源的时候,就进入Running状态执行线程方法了。如果线程数大于多处理器的数目,会存在多个线程轮换,尽管多个处理器会同时并行处理几个线程。

线程调度的细节取决于底层平台,当Running的线程调用其yield方法或失去CPU资源的时候,即回到Runnable状态。

Blocked

当发生如下情况,线程会被阻塞/重新进入Runnable状态:

1. 线程调用sleep等可中断方法            ===>    sleep方法经过指定时间/其它线程调用了阻塞线程的interrupt方法

2. 线程调用了一个阻塞式IO              ===>    调用的阻塞式IO方法返回

3. 试图获取一个正被使用的同步锁           ===>    成功获取同步锁

4. 调用了wait/await方法               ===>    其它线程发出了notify/notifyAll/signal/signalAll

5. 调用了其它线程的join方法,进入阻塞         ===>     调用了join线程的线程执行体完成或死亡

6. 线程调用suspend方法(容易导致死锁,不建议使用) ===>     被调用了resume方法

Dead

当发生如下情况,线程结束

1. 线程执行体完成

2. 抛出未捕获的异常或错误

3. 直接调用stop方法(容易导致死锁,不建议使用)

可通过调用线程对象的isAlive方法,如果处于新建和死亡状态会返回false

线程管理

常用的线程管理包括设置后台线程、设置优先级、join、sleep、yield、以及interrupt

设置后台线程

setDaemon(true):设置为后台线程

isDaemon():用于判断指定线程是否为后台线程

设置优先级

setPriority(int priority):设置优先级

getPriority():获取优先级

1 public classThreadPriority {2

3 public static voidmain(String[] args) {4 Thread t1 = new Thread(()->

5 {6 while(true) {7 System.out.println("t11111");8 }9 }, "t1");10 t1.setPriority(Thread.NORM_PRIORITY);11

12 Thread t2 = new Thread(()->{13 while (true) {14 System.out.println("t22222");15 }16 });17 t2.setPriority(10);18

19 t1.start();20 t2.start();21 }22

23 }

View Code

join

在某个程序执行流中(如main线程),调用其它线程的join方法,调用线程(如main线程)将被阻塞,直到被join方法加入的线程执行完为止。

join线程可以理解为把一个问题分为若干小问题由不同的线程处理,在其它线程处理完毕后,再回到运行状态的概念。

1 public classThreadJoin {2

3 private static voidshortSleep() {4 try{5 TimeUnit.SECONDS.sleep(1);6 } catch(InterruptedException e) {7 e.printStackTrace();8 }9 }10

11 private static Thread create(intseq) {12 return new Thread(() ->{13 for (int i = 0; i < 10; i++) {14 System.out.println(Thread.currentThread().getName() + "#" +i);15 shortSleep();16 }17 }, String.valueOf(seq));18 }19

20 public static void main(String[] args) throwsInterruptedException {21

22 List threads = IntStream.range(1, 3).mapToObj(ThreadJoin::create).collect(Collectors.toList());23

24 threads.forEach(Thread::start);25 //在main线程中, 依次调用thread的join方法, main会进入阻塞, 等其它线程完成了再行继续

26 for(Thread thread : threads) {27 thread.join();28 }29

30 for(int i = 0; i < 10; i++) {31 System.out.println(Thread.currentThread().getName() + "#" +i);32 shortSleep();33 }34

35 }36

37 }

View Code

sleep

Thread类的静态方法,用于暂停线程的执行。一般建议使用1.5后新增的TimeUnit类来更好的暂停线程

1 public classThreadSleep {2

3 private static void sleep(intms) {4 try{5 TimeUnit.SECONDS.sleep(ms);6 } catch(InterruptedException e) {7 e.printStackTrace();8 }9 }10

11 public static voidmain(String[] args) {12

13 new Thread(() ->

14 {15 long startTime =System.currentTimeMillis();16 sleep(2);17 long endTime =System.currentTimeMillis();18 System.out.println(String.format("Total spend %d second", (endTime -startTime)));19 }).start();20

21 /*

22 * Thread sleep times depends on your system23 */

24 long startTime =System.currentTimeMillis();25 sleep(3);26 long endTime =System.currentTimeMillis();27 System.out.println(String.format("Main thread total spend %d second", (endTime -startTime)));28

29 }30 }

View Code

interrupt

interrupt方法,主要是另外一个线程用于打断一个阻塞状态的线程,使其重新进入Runnable状态。如果一个阻塞方法(如sleep、wait)可以被interrupt至Runnable状态,这些方法又叫“可中断方法”。interrupt包括3个方法:

1. void interrupt()

2. static boolean interrupted()

3. boolean isInterrupted()

1 public classThreadInterrupt {2

3 public static void main(String[] args) throwsInterruptedException {4 Thread thread = newThread() {5 public voidrun() {6 while(true) {7 try{8 /*

9 * sleep为可中断方法, 因此会擦除interrupt标志;10 * 如果注释掉sleep方法, thread收到中断信号后,interrupt标志不会被移除11 */

12 TimeUnit.MINUTES.sleep(2);13 } catch(InterruptedException e) {14 System.out.printf("I am be interrupted? %s\n", isInterrupted());15 }16 }17 }18 };19 thread.setDaemon(true);20 thread.start();21 //确保thread线程启动完毕

22 TimeUnit.SECONDS.sleep(2);23 System.out.printf("I am be interrupted? %s\n", thread.isInterrupted());24 //在main线程中, 调用线程对象thread的interrupt()方法, 中断thread线程的sleep阻塞状态.

25 thread.interrupt();26 //确保thread线程进入Runnable并被线程调度器调动

27 TimeUnit.SECONDS.sleep(2);28 System.out.printf("I am be interrupted? %s\n", thread.isInterrupted());29 }30 }

事实上,在Thread中维护了一个interrupt的flag,它对线程对象的影响在于:

1. 如果一个线程在运行过程中,另外一个线程调用interrupt方法,相当于似得interrupt的flag为true(上面的代码中,注释掉thread线程的sleep代码,可以看到28结果为true)

2. 如果一个线程因为可中断方法而导致的阻塞,另一个线程调用interrupt方法,阻塞线程捕获中断信号,会把interrupt的flag改为false,实现重置。

static boolean interrupted()方法除了判断线程是否中断外,会直接把interrupt的flag变为true。

1 public classThreadInterrupted {2

3 public static void main(String[] args) throwsInterruptedException {4

5 Thread thread = new Thread(() ->{6 while (true) {7 if(Thread.interrupted()) {8 System.out.println("Yes my interrupt flag has been removed!");9 }else{10 System.out.println("I am running");11 }12 }13 });14

15 thread.setDaemon(true);16 thread.start();17

18 //Main sleep to make sure thread start

19 TimeUnit.MILLISECONDS.sleep(2);20 thread.interrupt();21 //以下显示false

22 System.out.println("Main thread is interrupted? " +Thread.interrupted());23

24 Thread.currentThread().interrupt();25 //以下显示true

26 System.out.println("Main thread is interrupted? " +Thread.currentThread().isInterrupted());27

28 try{29 TimeUnit.MINUTES.sleep(2);30 } catch(Exception e) {31 System.out.println("I am be interrupted!!!!!!");32 }33 }34 }

yield

与sleep方法不同,yield方法只是让当前线程暂停一下,以便线程调度器操作线程调度。该方法不会让线程进入阻塞

1 public classThreadYield {2

3 private static Thread create(intindex) {4 try{5 TimeUnit.SECONDS.sleep(1);6 } catch(InterruptedException e) {7 e.printStackTrace();8 }9 return new Thread(()->

10 {11 if (index == 0) {12 Thread.yield();13 }14 System.out.println(index);15 });16 }17

18 public static voidmain(String[] args) {19 IntStream.range(0, 2).mapToObj(ThreadYield::create).forEach(Thread::start);20 }21 }

View Code

线程关闭

线程关闭不建议使用stop方法,一般来说,线程关闭分为正常和异常两种情况。本文不讨论线程执行体正常完成退出以及异常抛出这两种情况,就业务逻辑中介绍两种主动关闭线程的方法。

1. 通过捕获interrupt中断信号来关闭线程

1 public classInterruptThreadExit {2

3 public static void main(String[] args) throwsInterruptedException {4

5 Thread t = newThread() {6 @Override7 public voidrun() {8 System.out.println("I will start to work");9 //此处通过IsInterrupt或interrupted方法来判断中断状态, 以便控制while循环实现控制线程关闭的目的

10 while(!interrupted()) {11 //System.out.println(isInterrupted());

12 }13 System.out.println(isInterrupted());14 System.out.println("I will exit");15 }16 };17 t.start();18 TimeUnit.SECONDS.sleep(3);19 System.out.println("ready to send interrupt signal");20 t.interrupt();21 }22 }

2. 通过volatile设置关闭开关

更一般地,可以使用volatile的可见性特点,设置线程控制开关,来控制线程的关闭

1 /*

2 * 通过volatile修饰符, 设置开关来控制线程关闭3 */

4 public classFlagThreadExit {5

6 static class MyTask extendsThread{7 //使用volatile修饰一个boolean开关变量

8 private volatile boolean closed = false;9 public voidrun() {10 System.out.println("I will start to work");11 //通过开变量和中断标识, 以便控制while循环实现控制线程关闭的目的

12 while(!closed && !interrupted()) {13 //working

14 }15 System.out.println("I will exit");16 }17 //定义用于关闭的close方法, 设置开关变量和调用中断方法

18 public voidclose() {19 closed = true;20 interrupt();21 }22 }23

24 public static void main(String[] args) throwsInterruptedException {25 MyTask t = newMyTask();26 t.start();27 TimeUnit.SECONDS.sleep(2);28 System.out.println("use close method");29 t.close();30 }31 }

java 关闭守护线程_Java并发编程之线程生命周期、守护线程、优先级、关闭和join、sleep、yield、interrupt...相关推荐

  1. java中解决脏读_java并发编程学习之脏读代码示例及处理

    使用interrupt()中断线程     当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即 ...

  2. java线程同步的实现_Java并发编程(三) - 实战:线程同步的实现

    synchronized关键字 首先,来看一个多线程竞争临界资源导致的同步不安全问题. package com.example.weishj.mytester.concurrency.sync; /* ...

  3. java等待5秒_Java并发编程-主线程等待子线程解决方案

    主线程等待所有子线程执行完成之后,再继续往下执行的解决方案 public class TestThread extends Thread { public void run() { System.ou ...

  4. java 共享锁 独占锁_Java并发编程锁之独占公平锁与非公平锁比较

    Java并发编程锁之独占公平锁与非公平锁比较 公平锁和非公平锁理解: 在上一篇文章中,我们知道了非公平锁.其实Java中还存在着公平锁呢.公平二字怎么理解呢?和我们现实理解是一样的.大家取排队本着先来 ...

  5. java程序使用异步总线_JAVA并发编程基础

    CPU核心 核心(Die)又称为内核,是CPU最重要的组成部分.CPU中心那块隆起的芯片就是核心,是由单晶硅以一定的生产工艺制造出来的,CPU所有的计算.接受/存储命令.处理数据都由核心执行.各种CP ...

  6. java 等待几秒_Java并发编程synchronized相关面试题总结

    说说自己对于synchronized关键字的了解 synchronized关键字用于解决多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个 ...

  7. java中同步组件_Java并发编程(自定义同步组件)

    并发包结构图: 编写一个自定义同步组件来加深对同步器的理解 业务要求: * 编写一个自定义同步组件来加深对同步器的理解. * 设计一个同步工具:该工具在同一时刻,只允许至多两个线程同时访问,超过两个线 ...

  8. java futuretask 源码_java并发编程——FutureTask源码分析

    FutureTask的简单示例: FutureTask的应用场景,如果在当前线程中需要执行比较耗时的操作,但又不想阻塞当前线程时,可以把这些作业交给FutureTask,另开一个线程在后台完成,当当前 ...

  9. java类声明语句_Java面向对象编程-类的声明周期

    第十章 类的生命周期 10.1 Java虚拟机及程序的生命周期 当通过java命令运行一个Java程序时,就启动了一个Java虚拟机进程.Java虚拟机进程从启动到终止的过程,称为Java虚拟机的生命 ...

最新文章

  1. 平板就是生产力?东京大学研究者“辟谣”了,用纸笔记录,更有利于记忆
  2. ubuntu+终端不能启动mysql数据库_强制重启Ubuntu服务器后Mysql无法启动
  3. 使用Exceptionless记录日志
  4. 2020年推荐系统工程师炼丹手册RecSys-Long Paper版
  5. 静态工厂方法代替构造器实例_静态工厂方法与传统构造方法
  6. 史上最全java架构师技能图谱(上)
  7. python现在时间减去过去时间等于20分钟怎么写_获取当前时间减去10分钟的话SQL语句怎么写...
  8. 爬虫scrapy框架安装使用
  9. linux学习笔记1(第一本笔记)
  10. VB.NET数据库中插入数据
  11. 若干个数据首尾相连,构成一个圆环,找到连续的4个数之和最大的一段。 C++
  12. 常见的两种解空间 全排列与幂集
  13. Android 在PreferenceActivity 中移除一个Preference
  14. 地铁7号线路图_成都地铁7号线线路图_运营时间票价站点_查询下载
  15. 敏捷团队的病与药——阿里健康医药B2B团队敏捷转型手记
  16. 双显卡同时显示多个显示屏
  17. Packetfence 开源网络准入系统
  18. java,go,python
  19. 5.03GEN-B发布!PSP 2000v3/3000最新自制系统
  20. 斯密特正交化与QR分解

热门文章

  1. 【免费下载】2021年7月热门报告盘点(附热门报告列表及下载链接)
  2. 微信团队的深度学习框架deepx_core开源啦
  3. 震惊!原来leetcode竟然真的能中奖?
  4. (Object detection)目标检测从入门到精通——第五部分YOLO 算法
  5. 用java提示用户输入学生个数_编写程序,提示用户输入一个数N,然后显示1~N的所有偶数平方值,求大神看看我写的程序哪里有问题。。...
  6. 大数据城市规划 杨东_空头转多!前期大比例减仓的私募,目前开始加仓
  7. 添加class值_Java 虚拟机(二) - Class 文件结构
  8. 跨境电商将成全球贸易的主角,下半年跨境电商趋势是什么?
  9. 独立站牵手Tik Tok 打造下一个电商节点
  10. 逻辑回归python实现