本文部分摘自《Java 并发编程的艺术》

线程简介

1. 什么是线程?

现代操作系统在运行一个程序时,会为其创建一个进程,一个进程里可以创建多个线程。现代操作系统调度的最小单元是线程,也叫轻量级进程。这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能访问共享的内存变量。处理器在这些线程上高速切换,让使用者觉得这些线程在同时执行

2. 为什么使用多线程?

使用多线程的原因主要有以下几点:

更多的处理器核心

通过使用多线程技术,将计算逻辑分配到多个处理器核心上,可以显著减少程序的处理时间

更快的响应时间

有时我们会编写一些较为复杂的代码(主要指业务逻辑),可以使用多线程技术,将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列)。这样做的好处是响应用户请求的线程能够尽可能快地处理完成,缩短了响应时间

更好的编程模型

Java 已经为多线程编程提供了一套良好的编程模型,开发人员只需根据问题需要建立合适的模型即可

线程优先级

现代操作系统基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程分配到若干时间片,当线程的时间片用完了发生线程调度,并等待下次分配。线程分配到的时间片多少也就决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要多或少分配一些处理器资源的线程属性

在 Java 线程中,通过一个整型成员变量 priority 来控制优先级,优先级的范围从 1 ~ 10,在线程构建时可以通过 setPriority(int) 方法来修改优先级,默认优先级是 5,优先级高的线程分配时间片的数量要多于优先级低的线程。不过,在不同的 JVM 以及操作系统上,线程规划会存在差异,有些操作系统甚至会忽略线程优先级的设定

public class Priority {

private static volatile boolean notStart = true;

private static volatile boolean notEnd = true;

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

List jobs = new ArrayList();

for (int i = 0; i < 10; i++) {

int priority = i < 5 ? Thread.MIN_PRIORITY : Thread.MAX_PRIORITY;

Job job = new Job(priority);

jobs.add(job);

Thread thread = new Thread(job, "Thread:" + i);

thread.setPriority(priority);

thread.start();

}

notStart = false;

TimeUnit.SECONDS.sleep(10);

notEnd = false;

for (Job job : jobs) {

System.out.println("Job Priority : " + job.priority + ", Count : " + job.jobCount);

}

}

static class Job implements Runnable {

private int priority;

private long jobCount;

public Job(int priority) {

this.priority = priority;

}

@Override

public void run() {

while (notStart) {

Thread.yield();

}

while (notEnd) {

Thread.yield();

jobCount++;

}

}

}

}

运行该示例,在笔者机器上对应的输出如下

笔者使用的环境为:Win10 + JDK11,从输出可以看到线程优先级起作用了

线程的状态

Java 线程在运行的生命周期中可能处于下表所示的六种不同的状态,在给定的一个时刻,线程只能处于其中的一个状态

状态名称

说明

NEW

初始状态,线程被构建,但还没调用 start() 方法

RUNNABLE

运行状态,Java 线程将操作系统中的就绪和运行两种状态笼统地称作“运行中”

BLOCKED

阻塞状态,表示线程阻塞于锁

WAITING

等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)

TIME_WAITING

超时等待状态,该状态不同于 WAITING,它是可以在指定的时间自行返回的

TERMINATED

终止状态,表示当前线程已经执行完毕

线程在自身的生命周期中,并不是固定地处于某一状态,而是随着代码的执行在不同的状态之间进行切换

Daemon 线程

Daemon 线程是一种支持型线程,主要被用作程序中后台调度以及支持性工作。这意味着,当一个 Java 虚拟机中不存在 Daemon 线程的时候,Java 虚拟机将退出。可以调用 Thread.setDaemon(true) 将线程设置为 Daemon 线程

使用 Daemon 线程需要注意两点:

Daemon 属性需要在启动线程之前设置,不能在启动线程之后设置

在构建 Daemon 线程时,不能依靠 finally 块中的内容来确保执行或关闭清理资源的逻辑。因为在 Java 虚拟机退出时 Daemon 线程中的 finally 块并不一定会执行

启动和终止线程

1. 构造线程

在运行线程之前首先要构造一个线程对象,线程对象在构造的时候需提供线程需的属性,如线程所属的线程组、是否是 Daemon 线程等信息

2. 启动线程

线程对象在初始化完成之后,调用 start() 方法即可启动线程

3. 理解中断

中断可以理解为线程的一个标识位属性,标识一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了个招呼,其他线程可以通过调用该线程的 interrupt() 方法对其进行中断操作

线程通过检查自身是否被中断进行响应,线程通过 isInterrupted() 来进行判断是否被中断,也可以调用静态方法 Tread.interrupted() 对当前线程的中断标识位进行复位。如果线程已经处于终结状态,即时线程被中断过,在调用该对象的 isInterrupted() 时依旧会返回 false

许多声明抛出 InterruptedException 的方法在抛出异常之前,Java 虚拟机会先将该线程的中断标识位清除,然后抛出 InterruptedException,此时调用 isInterrupted() 方法将会返回 false

在下面的例子中,首先创建两个线程 SleepThread 和 BusyThread,前者不停地睡眠,后者一直运行,分别对两个线程分别进行中断操作,观察中断标识位

public class Interrupted {

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

// sleepThread 不停的尝试睡眠

Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");

sleepThread.setDaemon(true);

// busyThread 不停的运行

Thread busyThread = new Thread(new BusyRunner(), "BusyThread");

busyThread.setDaemon(true);

sleepThread.start();

busyThread.start();

// 休眠 5 秒,让 sleepThread 和 busyThread 充分运行

TimeUnit.SECONDS.sleep(5);

sleepThread.interrupt();

busyThread.interrupt();

System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());

System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());

// 防止 sleepThread 和 busyThreaad 立刻退出

SleepUtils.second(2);

}

static class SleepRunner implements Runnable {

@Override

public void run() {

while (true) {

SleepUtils.second(10);

}

}

}

static class BusyRunner implements Runnable {

@Override

public void run() {

while (true) {

}

}

}

}

输出如下

从结果可以看出,抛出 InterruptedException 的线程 SleepThread,其中断标识位被清除了,而一直忙碌运行的线程 BusyThread 的中断标识位没有被清除

4. 安全地终止线程

前面提到的中断操作是一种简便的线程间交互方式,适合用来取消或停止任务。除了中断以外,还可以利用一个 boolean 变量来控制是否需要停止任务并终止线程

下面的示例中,创建了一个线程 CountThread,它不断地进行变量累加,而主线程尝试对其进行中断操作和停止操作

public class Shutdown {

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

Runner one = new Runner();

Thread countThread = new Thread(one, "CountThread");

countThread.start();

// 睡眠一秒,main 线程对 CountThread 进行中断,使 CountThread 能够感知中断而结束

TimeUnit.SECONDS.sleep(1);

countThread.interrupt();

Runner two = new Runner();

countThread = new Thread(two, "CountThread");

countThread.start();

// 睡眠一秒,main 线程对 Runner two 进行中断,使 CountThread 能够感知 on 为 false 而结束

TimeUnit.SECONDS.sleep(1);

two.cancel();

}

private static class Runner implements Runnable {

private long i;

private volatile boolean on = true;

@Override

public void run() {

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

i++;

}

System.out.println("Count i = " + i);

}

public void cancel() {

on = false;

}

}

}

main 线程通过中断操作和 cancel() 方法均可使 CountThread 得以终止。这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,更加安全和优雅

java线程 属性_Java 线程基础相关推荐

  1. 【C++ 语言】线程 ( 线程创建方法 | 线程标识符 | 线程属性 | 线程属性初始化 | 线程属性销毁 | 分离线程 | 线程调度策略 | 线程优先级 | 线程等待 )

    文章目录 I 线程创建方法 II 线程执行函数 III 线程标识符 IV 线程属性 V 线程属性 1 ( 分离线程 | 非分离线程 ) VI 线程属性 2 ( 线程调度策略 ) VII 线程属性 3 ...

  2. linux内核线程优先级设置,Linux c 线程属性,线程优先级的修改

    Linux c 线程属性,线程优先级的修改 发布时间:2018-07-19 10:24, 浏览次数:1290 , 标签: Linux 线程属性的设置,网上找的文章总感觉不够全面,还是结合man手册查看 ...

  3. java+向前进一_Java 线程基础

    前言 线程并发系列文章: 熟练掌握线程原理与使用是程序员进阶的必经之路,网上很多关于Java线程的知识,比如多线程之间变量的可见性.操作的原子性,进而扩展出的Volatile.锁(CAS/Synchr ...

  4. java线程属性_Java 并发 线程属性

    Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线 ...

  5. java 线程不足_Java 线程基础知识

    wait() 和notify().notifyAll() 这三个方法用于协调多个线程对共享数据的存取,所以必须在 Synchronized 语句块内使用这三个方法,否则会抛出错 IllegalMoni ...

  6. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(一)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  7. java线程分类_Java 线程类别

    Java 线程类别 守护线程和非守护线程 守护线程和非守护线程之前的唯一区别在于:是否阻止JVM的正常退出. JVM正常退出是与异常退出相对的概念,异常退出如调用System.exit(status) ...

  8. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(二)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  9. java线程池_Java 线程池 8 大拒绝策略,面试必问!

    点击上方 Java后端,选择设为星标 技术博文,及时送达 前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的 ...

最新文章

  1. python中保留两位小数的编写程序_Python中保留两位小数的几种方法
  2. Spring Cloud构建分布式电子商务平台:服务消费(基础)
  3. C#非泛型集合类-ArrayList数组集合类
  4. Angular中使用双向数据绑定操作表单的input、checkboc、radio、select、textarea实现简单的人员登记系统实例
  5. python以运行效率高著称吗_几个提升Python运行效率的方法之间的对比
  6. 机器学习速成课程 | 练习 | Google Development——编程练习:提高神经网络的性能
  7. python递归中的return_「问题思考」python的递归中return返回none
  8. 诺基亚正式收购阿朗:物联网时代再出发
  9. 简单的计算机英语文章,简单的英语小短文欣赏
  10. Assembly Manifest 通俗简易手册
  11. 【冷门】十字链表法图解
  12. FLV格式解析及其解析器的实现
  13. SpringBoot使用LibreOffice--office转pdf
  14. MPC5607B串口接收中断程序
  15. ‘git clone‘ failed with status 128
  16. 动物识别 羊群识别 牛识别 马识别 yolo动物识别 鸟类识别 狗识别 猫狗分类
  17. IT、TT、TN系统,你真的了解吗?
  18. Android UI性能优化 检测应用中的UI卡顿
  19. 一些网站...........
  20. 一文看懂HTTPS、证书机构(CA)、证书、数字签名、私钥、公钥

热门文章

  1. 不学网php,php不学框架可以吗
  2. pythonqt5plaintextedit某一行的内容_如何能够做到持续输出内容?
  3. fragment photoshop_史上最接地气的Photoshop?谈PS 2021的黑科技 - Adobe
  4. php调用restful接口_如何使用PHP编写RESTful接口
  5. 海豚计算机语言,海豚模拟器 V5.0 多国语言安装版
  6. php创建实例对象数组,php – 使用arguments数组创建新的对象实例
  7. java主类调用数组_用java编写在主函数中调用数组
  8. php 如何保存录像文件,如何录制屏幕并保存下来?
  9. java 返回语句_Java:“缺少返回语句”
  10. 帆软报表在已经搭载服务器上开发_史上最全企业数据产品选型对比(含数仓、报表、BI、中台、数据治理)...