在前面的《兵分三路:如何创建多线程》文章中,我们已经通过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中的线程的更多相关文章

  1. 并发王者课-青铜7:顺藤摸瓜-如何从synchronized中的锁认识Monitor

    在前面的文章中,我们已经体验过synchronized的用法,并对锁的概念和原理做了简单的介绍.然而,你可能已经察觉到,有一个概念似乎总是和synchronized.锁这两个概念如影相随,很多人也比较 ...

  2. 并发王者课 - 青铜4:synchronized用法初体验

    在前面的文章<双刃剑-理解多线程带来的安全问题>中,我们提到了多线程情况下存在的线程安全问题.本文将以这个问题为背景,介绍如何通过使用synchronized关键字解这一问题.当然,在青铜 ...

  3. 并发王者课-青铜5:一探究竟-如何从synchronized理解Java对象头中的锁

    在前面的文章<青铜4:synchronized用法初体验>中,我们已经提到锁的概念,并指出synchronized是锁机制的一种实现.可是,这么说未免太过抽象,你可能无法直观地理解锁究竟是 ...

  4. (6)简单说说java中的线程

    先甩出来两种创建线程的方法: private static int count = 100; public static void main(String[] args) { // 用继承Thread ...

  5. 并发王者课-铂金8:峡谷幽会-看CyclicBarrier如何跨越重峦叠嶂

    欢迎来到<并发王者课>,本文是该系列文章中的第21篇,铂金中的第8篇. 在上一篇文章中,我们介绍了CountDownLatch的用法.在协调多线程的开始和结束时,CountDownLatc ...

  6. 并发王者课-铂金9:互通有无-Exchanger如何完成线程间的数据交换

    欢迎来到<并发王者课>,本文是该系列文章中的第22篇,铂金中的第9篇. 在前面的文章中,我们已经介绍了ReentrantLock,CountDownLatch,CyclicBarrier, ...

  7. 并发王者课-铂金10:能工巧匠-ThreadLocal如何为线程打造私有数据空间

    欢迎来到<并发王者课>,本文是该系列文章中的第23篇,铂金中的第10篇. 说起ThreadLocal,相信你对它的名字一定不陌生.在并发编程中,它有着较高的出场率,并且也是面试中的高频面试 ...

  8. 并发王者课-铂金1:探本溯源-为何说Lock接口是Java中锁的基础

    欢迎来到<并发王者课>,本文是该系列文章中的第14篇. 在黄金系列中,我们介绍了并发中一些问题,比如死锁.活锁.线程饥饿等问题.在并发编程中,这些问题无疑都是需要解决的.所以,在铂金系列文 ...

  9. 并发王者课-铂金2:豁然开朗-“晦涩难懂”的ReadWriteLock竟如此妙不可言

    欢迎来到<并发王者课>,本文是该系列文章中的第15篇. 在上篇文章中,我们介绍了Java中锁的基础Lock接口.在本文中,我们将介绍Java中锁的另外一个重要的基本型接口,即ReadWri ...

随机推荐

  1. HarmonyOS三方件开发指南(15)-LoadingView功能介绍

    目录: 1. LoadingView组件功能介绍2. Lottie使用方法3. Lottie开发实现4.<HarmonyOS三方件开发指南>系列文章合集 1. LoadingView组件功 ...

  2. vue封装公用弹出框方法,实现点击出现操作弹出框

    vue封装公用弹出框方法,实现点击出现操作弹出框 如上图所示,这次要实现一个点击出现操作弹框的效果:并将这个功能封装成一个函数,便于在项目的多个地方使用. 具体思路是: 封装一个组件,组件保护一个插槽 ...

  3. go中semaphore(信号量)源码解读

    运行时信号量机制 semaphore 前言 作用是什么 几个主要的方法 如何实现 sudog 缓存 acquireSudog releaseSudog semaphore poll_runtime_S ...

  4. Topshelf一个用于使用.NET构建Windows服务框架

    1 Topshelf是什么? Topshelf是用于托管使用.NET框架编写的Windows服务的框架.服务的创建得到简化,从而使开发人员可以创建一个简单的控制台应用程序,可以使用Topshelf将其 ...

  5. Python基础之:Python中的内部对象

    目录 简介 内置函数 内置常量 内置类型 逻辑值检测 逻辑值的布尔运算 比较运算 数字类型 整数类型的位运算 整数类型的附加方法 浮点类型的附加方法 迭代器 序列类型 集合类型 映射类型 字典视图对象 ...

  6. Spring IOC 特性有哪些,不会读不懂源码!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 多线程.锁.JVM调优,都背出花啦,怎么一写代码还是乱糟糟? 为什么这些无论从书本. ...

  7. HTML5新增语法

    ##1.video1.简化版写法:兼容性差```<video src="" controls> </video>``` 2.视频标签标准语法(兼容处理)`` ...

  8. ubuntu20.04开机显示recovering journal死机的解决方法

    事发突然,在今天开机的时候无法进入登陆界面,一直卡在黑屏界面,屏幕上只显示几行代码,且任何按键都无法起作用 /dev/sdb2:recovering journal /dev/sdb2:Clearin ...

  9. Crackme_003

    功能: 拿到文件,先执行一下.功能如下: 1.nag窗口 会先出现如下nag窗口,持续几秒 2.注册窗口: 出现错误会提示:You Get Wrong  Try Again 破解: 1.查壳: 无壳, ...

  10. CTF导引(一)

    ctf预备知识: 视频:https://www.bilibili.com/video/av62214776?from=search&seid=1436604431801225989 CTF比赛 ...