青铜2:本来面目-如何认识Java中的线程
在前面的《兵分三路:如何创建多线程》文章中,我们已经通过Thread和Runnable直观地了解如何在Java中创建一个线程,相信你已经有了一定的体感。在本篇文章中,我们将基于前面的示例代码,对线程做些必要的说明,以帮助你从更基础的层面认知线程,并为后续的学习打下基础。
一、从进程认知线程
在上世纪的80年代中期之前,进程一直都是操作系统中拥有资源和独立运行的基本单位。可是,随着计算机的发展,人们对操作系统的吞吐量要求越来越高,并且多处理器也逐渐发展起来,进程作为基本的调度单位已经越来越不合时宜,因为它太重了。
想想看,我们在创建进程的时候,需要给它们创建PCB,并且还要分配需要的所有资源,如内存空间、I/O设备等等。然而,在进程切换时,系统还需要保留当前进程的CPU环境并设置新的进程CPU环境,这些都是需要花费时间的。也就是说,在进程的创建、切换以及销毁的过程中,系统要花费巨大的时空开销。如此,在一个操作系统中,就不能设置过多数量的进程,并且还不能频繁切换。显然,这不符合时代的发展需要了。
因此,进程切换的巨大开销和多核CPU的发展,线程便顺势而生。
从概念上,线程可以理解为它是操作系统中独立运行的基本单元,一个进程可以拥有多个线程。并且,和进程相比,线程拥有进程很多相似属性,因此线程有时候也被称为轻量级进程(Light-Weight Process)。
为什么线程相对轻量
在线程之前,系统切换任务时需要切换进程,开销巨大。然而,引入线程后,线程隶属于进程,进程仍是资源的拥有者,线程只占据少量的资源。同时,线程的切换并不会导致进程的切换,因此开销较小。此外,进程和线程都可以并发执行,操作系统也因此获得了更好的并发性,也能有效地提高系统资源的利用率和系统吞吐量。
二、Thread类-Java中的线程基础
在本文的第一部分,我们从操作系统层面认识了线程。而在Java中,我们则需要通过Thread类认知线程,包括它的一些基本属性和基本方法。Thread是Java多线程基础中的基础,请不要因为它简单就忽略这部分的内容。
下面的这幅图概括展示了Thread类的核心属性和方法:
1. Thread中如何构造线程
Thread中共有9个公共构造器,当然我们不用掌握全部的构造,熟悉其中几个比较常用的即可:
Thread()
,这个构造器会默认生成一个Thread-
+n
的名字,n
是由内部方法nextThreadNum()
生成的一个整型数字;Thread(String name)
,在构建线程时指定线程名,是一个很不错的实践;Thread(Runnable target)
,传入Runnable
的实例,这个我们在上一篇文章中已经展示过;Thread(Runnable target, String name)
,在传入Runnable
实例时指定线程名。
2. Thread中的关键属性
从init()
方法理解Thread的构造
虽然Thread有9个构造函数,但最终都是通过下面的这个init()
方法进行构造,所以了解了这个方法,就了解了Thread的构造过程。
/*** Initializes a Thread.** @param g the Thread group* @param target the object whose run() method gets called* @param name the name of the new Thread* @param stackSize the desired stack size for the new thread, or* zero to indicate that this parameter is to be ignored.* @param acc the AccessControlContext to inherit, or* AccessController.getContext() if null* @param inheritThreadLocals if {@code true}, inherit initial values for* inheritable thread-locals from the constructing thread*/private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals)
init()
方法几十行的代码,不过为了节省篇幅,我们此处只贴出了方法的签名,具体的方法体内容可以自行去看。在方法的签名中,有几个重要的参数:
g
:线程所属的线程组。线程组是一组具有相似行为的线程集合;target
:继承Runnable
的对象实例;name
:线程的名字;
其他几个参数通常不需要自定义,保持默认即可。如果你翻看init()
的方法体代码,可以看到init()
对下面几个属性做了初始化:
name
:线程的名字;group
:线程所属的线程组;daemon
:是否为守护进程;priority
:线程的优先级;tid
:线程的ID;
关于名字
虽然Thread默认会生成一个线程名,但为了方便日志输出和问题排查,比较建议你在创建线程时自己手动设置名称,比如anQiLaPlayer
的线程名可以设置为Thread-anQiLa
。
关于线程ID
和线程名一样,每个线程都有自己的ID,如果你没有指定的话,Thread会自动生成。确切地说,线程的ID是根据threadSeqNumber()
对Thread的静态变量threadSeqNumber
进行累加得到:
private static synchronized long nextThreadID() {return ++threadSeqNumber;
}
关于线程优先级
在创建新的线程时,线程的优先级默认和当前父线程的优先级一致,当然我们也可以通过setPriority(int newPriority)
方法来设置。不过,在设置线程优先级时需要注意两点:
- Thread线程的优先级设置是不可靠的:我们可以通过数字来指定线程调度时的优先级,然而最终执行时的调度顺序将由操作系统决定,因为Thread中的优先级设置并不是和所有的操作系统一一对应;
- 线程组的优先级高于线程优先级:每个线程都会有一个线程组,我们所设置的线程优先级数字不能高于线程组的优先级。如果高于,将会直接使用线程组的优先级。
3. 线程中的关键方法
Thread中几个重要的方法,如start()
、join()
、sleep()
、yield()
、interrupted()
等,关于这几种方法的用法,我们会在下一篇文章中结合线程的状态进行讲解。需要注意的是,notify()
、wait()
等并不是Thread类的方法,它们是Object的方法。
三、多线程的应用场景
通过前面的分析,我们已经从操作系统层面和Java中认知了线程。那么,什么样的场景需要考虑使用多线程?
总的来说,当你遇到以下两类场景时,需要考虑多线程:
1. 异步
当两个独立逻辑单元不需要同步顺序完成时,可以通过多线程异步处理。
比如,用户注册后发送邮件消息。很显然,注册和发送消息是两个独立逻辑单元,在注册完成后,我们可以另起线程完成消息的发送,从而实现逻辑解耦并缩短注册单元的响应时间。
2. 并发
现在的计算机基本都是多核处理器,在处理批量任务时,可以通过多线程提高处理速度。
比如,假设系统需要向100万的用户发送消息。可以想象,如果单线程处理不知道猴年马月才能完成。而此时,我们便可以通过线程池创建多线程大幅提高效率。
注意,对于一些同学来说,你可能还没有接触过多线程的应用场景。但是,请不要因为工作中的场景简单或数据量较低就忽视多线程的应用,多线程在你身边的各类中间件和互联网大厂中都有着极为广泛的应用。
应用多线程时的风险提示
虽然多线程有很多的好处,但仍然要根据场景客观分析,对多线程不合理的使用会增加系统的安全风险和维护风险。所以,在使用多线程时,请务必确认场景的合理性,以及它在你技术能力掌控之中。
以上就是文本的全部内容,恭喜你又上了一颗星!
夫子的试炼
- 使用不同的构造方式,编写两个线程并打印出线程的关键信息;
- 检索资料,详细比对进程与线程的区别。
关于作者
关注公众号【庸人技术笑谈】,获取及时文章更新。记录平凡人的技术故事,分享有品质(尽量)的技术文章,偶尔也聊聊生活和理想。不贩卖焦虑,不兜售课程。
青铜2:本来面目-如何认识Java中的线程相关推荐
- java中的线程安全是什么?
java中的线程安全是什么: 就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法进行访问. 什么 ...
- 关于Java中的线程安全(线程同步)
java中的线程安全是什么:就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法进行访问 什么叫线 ...
- 四十七、面试前,必须搞懂Java中的线程池ThreadPoolExecutor(上篇)
@Author:Runsen @Date:2020/6/9 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...
- Java 中的线程安全的类
Java 中的线程安全的类 3 个线程安全的类 它们对应的非线程安全的类 它们延伸的类 其它 它们对应的非线程安全的类 3 个线程安全的类 Java 中的 3 个基本的线程安全的类为:Hashta ...
- 不允许使用java方式启动_细品 Java 中启动线程的正确和错误方式
细品 Java 中启动线程的正确和错误方式 前文回顾详细分析 Java 中实现多线程的方法有几种?(从本质上出发) start 方法和 run 方法的比较 代码演示:/** * * start() 和 ...
- 如何在java中实现线程_用代码说话:如何在Java中实现线程
并发编程是Java语言的重要特性之一,"如何在Java中实现线程"是学习并发编程的入门知识,也是Java工程师面试必备的基础知识.本文从线程说起,然后用代码说明如何在Java中实现 ...
- Java中一个线程只有六个状态。至于阻塞、可运行、挂起状态都是人们为了便于理解,自己加上去的。...
java中,线程的状态使用一个枚举类型来描述的.这个枚举一共有6个值: NEW(新建).RUNNABLE(运行).BLOCKED(锁池).TIMED_WAITING(定时等待).WAITING(等待) ...
- 万字图文 | 学会Java中的线程池,这一篇也许就够了!
来源:一枝花算不算浪漫 线程池原理思维导图.png 前言 Java中的线程池已经不是什么神秘的技术了,相信在看的读者在项目中也都有使用过.关于线程池的文章也是数不胜数,我们站在巨人的肩膀上来再次梳理一 ...
- java线程和内核线程的,Java中内核线程理论及实例详解
1.概念 内核线程是直接由操作系统内核控制的,内核通过调度器来完成内核线程的调度并负责将其映射到处理器上执行.内核态下的线程执行速度理论上是最高的,但是用户不会直接操作内核线程,而是通过内核线程的接口 ...
最新文章
- 逆袭之旅DAY17.东软实训.Oracle.存储过程
- oracle 新建路径,Linux环境安装Oracle11g(三)——用户、路径创建及配置环境变量
- 信号处理常用算法介绍
- 视觉slam十四讲 pdf_视觉SLAM十四讲|第12讲 回环检测
- Mobvista首席架构师蔡超:工作感悟之失败与成功,我的8点总结
- 从问题出发,解密Oracle rdba结构
- html 判断当前窗口是否是子窗口,JavaScript window.open 判断子窗口是否已经存在
- 为什么有人执着于只买黑色的手机?
- Oracle会话管理
- 代码行数统计工具,java,go,c++,html文件都适用
- 1.(MySQL Router+MGR)MySQL Shell安装与用法
- JScript服务器运行,jscript模拟的“控制台”程序Web服务器教程
- 文件是否存在 FileExists 方法
- 使用Xilinx XSCT工具进行烧录
- 利用黑盒测试技术设计测试用例
- 传感器的使用,高仿微信摇一摇,动画加声音
- Red Gate - SQL Source Control实现对SQL SERVER 的源代码控制
- Unity游戏存档 (将游戏数据储存至本地文档)
- 老贼远古VOD自动入库使用说明
- android流量监控软件设计与实现,Android平台流量监控软件的设计与实现