【JavaEE初阶一】线程的概念与简单创建

1. 认识线程(Thread)

1.1 关于线程

1.1.1 线程是什么

        由前一节的内容可知,进程在进行频繁的创建和销毁的时候,开销比较大(主要体现在资源的申请和释放上),线程就是为了解决上述产生的问题而提出的方案;线程保持了独立调度执行,这样的“并发支持”,如此同时省去“分配资源”“释放资源”带来的额外开销。

        一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行 着多份代码, 一个进程中可以并发多个线程,每条线程并行执行不同的任务。

1.1.2  为啥要有线程

1、首先, "并发编程" 成为 "刚需".

        单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源. 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编 程.

2、其次, 虽然多进程也能实现 ,并发编程, 但是线程比进程更轻量.

        创建线程比创建进程更快.

        销毁线程比销毁进程更快.

        调度线程比调度进程更快.

3、最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 "线程池"(ThreadPool) 和 "协程" (Coroutine)

1.2 进程与线程

1.2.1 简单讲解

        下面来可能错误的分析一下进程与线程在创建和销毁的时候对内存地址的利用:

         下图我们用pcb来描述一个进程,图解如下:

        如上图所示,每一个进程在创建和销毁时都会在内存空间操作自己的地址,在进行多进程并发执行的时候,多个进程大规模的创建和销毁会再一定程度上消耗系统和操作系统的资源;

        下图我们来用pcb描述一下线程,图解如下图所示:

        pcb中有一个属性就是内存指针,如上图所示,多个线程的内存指针都指向内存地址中的同一个位置;

        这就意味着以上多个线程只有在第一个线程创建的时候需要从系统中分配资源,后序的线程就不需要继续在分配资源了,直接公用前面的那份资源就行了;

        同时除了内存之外,文件描述符(操作硬盘),也是多个线程共用一份的;当然我们也要注意不是所有的线程都能实现如上程度的资源共享的,只有我们设置的线程组才能实现资源共享;

        综上:线程的出现解决了频繁申请和释放资源的开销

 1.2.2 两者的关系

       没有进程的时候,进程扮演两个角色(资源分配的基本单位调度执行的基本单位

        引入了线程之后,进程只需要扮演一个角色(资源分配的基本单位),线程分担了一个角色的(调度执行的基本单位

        关于线程和进程的小结:

1、进程是包含线程的;

2、每一个线程也是一个独立的执行流,且可以执行一段代码,并且单独的参与到cpu调度中(状态,上下文,优先级,记账信息,每一个线程都有自己的一份)

3、每个进程有着自己独有的资源,进程中的线程公用这一份资源(内存空间和文件描述符)----->(进程是资源分配的基本单位,线程是调度执行的基本单位)

4、进程和进程之间,不会相互影响;如果一个进程的某个线程抛出异常,是可能会影响到其他线程的,由此会把整个进程中的所有线程都异常终止;

5、同一个进程中的线程之间,可能会相互干扰,从而引起线程安全问题;

6、当然线程不是越多越好,要能够合适,如果线程太多的话,调度开销就可能十分明显;

1.3 java线程和操作系统之间的关系

        线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库).

        Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装.

        所谓多线程编程:

        写代码的时候可以使用多线程进行并发编程(在java中不太推荐,很多和多线程编程相关的api在java标准库中都没有提供),也可以使用多线程并发编程(系统提供了多线程编程的api,java的标准库把这些api封装了,如此在代码中就可以使用了

        由此可以得出多线程在并发编程的时候,效率更高(频繁创建销毁的时候),尤其是对于java进程,是要启动java虚拟机的,如果启动java虚拟机,则这个事情的开销更大,--->可类似的看成搞多个java进程就是多个java虚拟机。

2. 初识多线程程序

        首先要有一个注意的点:

  • 每个线程都是一个独立的执行流
  • 多个线程之间是 “并发” 执行的

2.1 详细代码

        代码一:通过代码详细了解thread类:

        Java中提供的api,是通过thread这样的类进行展开的。

package thread;

// 1. 创建一个自己的类, 继承自这个 Thread
class MyThread extends Thread {
    @Override
    public void run() {
        // run 方法就是该线程的入口方法.
        System.out.println("圣诞节快乐,委婉待续!!!");
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        // 2. 根据刚才的类, 创建出示例. (线程实例化, 才是真正的线程).
        // MyThread t = new MyThread();
        Thread t = new MyThread();
        // 3. 调用 Thread 的 start 方法, 才会真正调用系统 api, 在系统内核中创建出线程.
        t.start();
    }
}

        结果如下:

        代码二:编写mythread自定义类线程,在主线程中运行该自定义线程,代码如下:

package thread;
//1、创建一个自己的类,继承自这个thresd
class MyThread2 extends Thread{
    //重写run方法
    @Override
    public void run() {
        //run 方法就是该线程的入口方法
        while (true){
            System.out.println("hello thread,委婉待续");
            try {
                Thread.sleep(1000);//休眠1000ms
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class ThreadDemo2 {
    //创建一个main方法
    public static void main(String[] args){
        //2、根据刚才的类,创建出实例(线程实例,才是整整的线程)
        MyThread2 t = new MyThread2();
        //3、调用thread的start方法,才会整整的调用系统的api,在系统的内核中创建出线程池,
        //然后线程就开始运行我们的run方法中的代码
        t.start();
        while (true){
            System.out.println("hello main,smallye");
            try {
                Thread.sleep(1000);//休眠1000ms
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

        结果显示:

 

        结果显示交替进行,这就展现出了多线程与普通程序的区别

 2.2 代码深入分析:

2.2.1 一些问答

Q1:关于上图Thread类为啥能直接使用,而不需要进行导包?

A1:在java标准库中,有一个特殊的包,java.lang(这个包时默认被导的,这里面的类默认使用)

Q2:为啥我们自定义类mythread2前面没有public?可以加public吗?

A2:不能,一个.java文件中,只能有一个public的类,这个mythread类没有被public修饰,就是只能在当前包里被其他的类使用。

Q3:讲解一下run方法与main方法的区别?

A3:上图就类似于main方法,该run方法是一个java进程(程序)的入口方法(一般将“跑起来”的程序称为进程,没有运行起来的程序(.exe),称为“可执行文件”),且此处的run方法,不需要手动调用,会在合适的时候(此时是线程创建好了之后,即被实例化后),被jvm自动调用执行。(如此风格的函数,称为“回调函数callback”)

Q4:以上字符代表的含义?关于方法重写的含义?

A4:

1、首先上图字符是方法重写的注解,主要目的就是方便让编译器检查我们的代码是否构成方法重写(语法中有很多机制,就是让编译器对我们的代码进行检查,如果我们明确该方法是重写的,有了这个注解编译器就会检查我们的方法是否满足方法的重写,参数等是否满足方法重写的要求,这样就能够及时的报错,大大的提高了我们的工作效率)

2、方法重写:就是让你能够对现有的类,进行扩展,写出符合场景需求的具体方法。

我们写的以上线程,肯定是让这个线程执行一些代码的。Thread类本身就会带有一个人入口方法,但是很明显标准库自带的run是不知道我们的需求业务是啥样的,所以我们必须要手动指定(即写出一个具体的业务),这样就需要针对原有的Thread进行扩展,Thread会有很多属性方法,大部分内容复用即可,只要把需要扩展的内容进行扩展即可。

Q5:thread.sleep的作用?

A5:所谓sleep是java中封装后版本中thread中提供的静态方法,其主要作用就是让当前的线程进行休眠,时间单位是ms;

        当然sleep会出现java.lang.interruptedException异常,该异常出现的原因是我们要求休眠1000ms的线程会由于其他原因导致提前被唤醒,不能够休眠1000ms

Q5.1:下图中两个线程中的sleep为什么只有前者可以进行try-catch捕捉,而后者既可以进行try-catch捕捉,也可以进行抛出方法签名?

A5.1:正常情况下,一般受查异常既可以添加异常方法签名也可以使用try-catch捕捉;但是我们的前者sleep所在的mythread类中的run方法是要经过重写操作是具体实现的方法,如果我们添加了方法签名,那么由于语法所致,会让我们的方法不能构成重写操作;更有父类thread的run没有throws异常,所以子类重写的时候也不能有throws异常;

 2.2.2 多线程的运行逻辑

        如上图所示:

        在main方法中,主线程调用start方法(创建了t线程),此时cpu的两个核心开始运转,兵分两路,一方面执行沿着mian方法继续执行,即打印“hello main,smallye”;另外一方面,内核就通过刚才主线程api构造出t线程,并且执行run方法,即打印“hell0 thread,委婉待续”;同时这两个线程在同时执行的时候各论各的,互不干扰。

        但是正因为这样,所以考虑两个线程的执行顺序是一样的吗?

        其实这两个线程的执行顺序是不一样的,因为在操作系统的内核中,有“调度器”模块,该模块实现的方式是一种类似于“随机调度的”效果;

        所谓随机调度会导致以下两个后果:

        1、一个线程,被调到cpu上执行的时机是不确定的;

        2、上位到cpu里被执行的线程从cpu上下来的给别的线程上位的时机也是不确定的,如此就会导致线程“抢占式执行”,且当前的主流操作系统都是抢占式执行的;

        由于此案成创建本身是有开销的,故此在该开销本身的影响下,导致“hello main,smallye”会比“hell0 thread,委婉待续”快一点(大概率,但是不一定),综合题前所学,进程创建第一线程的时候开销是最大的,剩下的线程的开销都计较少;

2.3 使用 jconsole 命令观察线程

        我们可以使用jdk自带的工具 jconsole查看当前Java进程中所有的线程

        操作流程如下:

1、第一步,找到jdk

2、第二步,点进去,找到里面的bin文件点进去

3、第三步,点击bin文件夹里面的jconsole

4、第四步,找到你所创建进程

5、第五步,直接点击不安全连接就好

6、第六步,点击线程进行查看

        我们的t是指线程的变量名,所谓的看到的thread—0,是我们自定义线程的默认名字,一般会从0~n;

ps:本次的内容就到这里了,如果感兴趣的话就请一键三连哦!!!

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/269783.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件测试 —— 如何测试图片上传功能?

作为一名专业的软件测试人员,测试图片上传功能是一个重要的任务,以下是一些测试该功能的常用方法: 1. 上传功能测试:确保图片上传功能正常工作,包括选择图片文件、点击上传按钮、上传进度显示、上传成功/失败的提示等。…

postman的下载安装和使用

第一章、使用postman向后端发送请求 1.2)postman下载与安装使用 我的百度网盘postman点击下载 提取码:bybp 下载后双击.exe文件直接安装 点击此次创建集合 点击此处创建请求 1.2)发送get请求 选择自己的请求方式,输入请求…

1861_什么是H桥

Grey 全部学习内容汇总: GitHub - GreyZhang/g_hardware_basic: You should learn some hardware design knowledge in case hardware engineer would ask you to prove your software is right when their hardware design is wrong! 1861_什么是H桥 H桥电路可以…

Qt动态连接库/静态连接库创建与使用,QLibrary动态加载库

问题:下图中是一个生成库的模块,其中qglobal.h的使用有点疑惑,可以参考下文链接。 ​​​​​​​​​​​​https://www.cnblogs.com/techiel/p/8035014.htmlhttps://www.cnblogs.com/techiel/p/8035014.html

Redis过期删除策略和内存淘汰策略

1、设置Redis键过期时间 Redis提供了四个命令来设置过期时间&#xff08;生存时间&#xff09;。 EXPIRE <key> <ttl> &#xff1a;表示将键 key 的生存时间设置为 ttl 秒。 PEXPIRE <key> <ttl> &#xff1a;表示将键 key 的生存时间设置为 ttl 毫秒。…

算法基础之最长上升子序列 II

最长上升子序列 II 核心思想&#xff1a;不去遍历全部的数据(会有冗余) 用vector模拟栈 ①如果该元素大于栈顶元素,将该元素入栈 ②替换掉第一个大于或者等于这个数字的那个数&#xff08;二分&#xff09; #include<iostream>#include<algorithm>#include&l…

Kafka日志文件存储

日志文件 kafka在server.properties配置文件中通过log.dir属性指定了Kafka的日志存储路径 核心文件 1. log文件 实际存储消息的日志文件, 大小固定1G(参数log.segment.bytes可配置), 写满后就会新增一个新的文件, 文件名是第一条消息的偏移量 2. index文件 以偏移量为索引…

4.9【共享源】流的多生产者和消费者

当一个系统中存在多个生产者和消费者时&#xff0c;情况可能会变得复杂。 了解生产者和消费者流之间支持的基数非常重要。 本质上&#xff0c;一个生产者流可以与多个消费者流连接&#xff0c;但一个消费者流只能连接到一个生产者流。请注意&#xff0c;基数关系仅限于单个流&…

3D渲染农场什么比较好用 2024渲染农场最新收费实测

随着数字设计领域的进步与发展&#xff0c;对于3D渲染服务的需求日益增加。3D渲染农场这一概念因此变得极为重要&#xff0c;特别是在电影制作、建筑可视化以及产品设计等行业中。现在&#xff0c;让我们深入了解3D渲染农场的定义以及市面上优秀的3D渲染服务提供商。 一、什么是…

Ai画板原理

在创建时画板可以选择数量和排列方式 也可以采用这个图片左上的画板工具&#xff0c;选择画板在其他地方画框即可生成&#xff0c;同时可以在属性框中可以修改尺寸大小 选择全部重新排列可以进行创建时的布局

STM32 支持IAP的bootloader开发,使用串口通过Ymodem协议传输固件

资料下载: https://download.csdn.net/download/vvoennvv/88658447 一、概述 关于IAP的原理和Ymodem协议&#xff0c;本文不做任何论述&#xff0c;本文只论述bootloader如何使用串口通过Ymodem协议接收升级程序并进行IAP升级&#xff0c;以及bootloader和主程序两个工程的配置…

yolo实现数据增强(数据集不够,快速增加数据集)

目录结构 附上数据增强的全部代码 # -*- codingutf-8 -*-import time import random import copy import cv2 import os import math import numpy as np from skimage.util import random_noise from lxml import etree, objectify import xml.etree.ElementTree as ET imp…

makefile教程(1)

makefile教程 makefile是什么&#xff1a; makefile是用户自行完成的IDE&#xff08;integrated development environment集成开发环境&#xff09;程序&#xff0c;与传统的操作系统下的编译不同&#xff0c;makefile可以通过用户自行安排&#xff0c;决定文件的编译顺序&am…

将elementUI,NaiveUI的progress环形进度条设置为渐变色

需求 &#xff1a;进度条要有一个渐变效果。效果图&#xff1a; NaiveUI和elementUI的官方progress组件都是只能设置一种颜色&#xff0c;不符合需求所以改一下。 其实NaiveUI和elementUI设置进度条的实现方式基本一样都是使用svg渲染出两个path&#xff0c;第一个是底色&…

ssh工具 向指定的ssh服务器配置公钥

此文分享一个python脚本,用于向指定的ssh服务器配置公钥,以达到免密登录ssh服务器的目的。 效果演示 🔥完整演示效果 👇第一步,显然,我们需要选择功能 👇第二步,确认 or 选择ssh服务器 👇第三步,输入ssh登录密码,以完成公钥配置 👇验证,我们通过ssh登录…

如何使用Docker将.Net6项目部署到Linux服务器(二)

目录 二 安装Redis 2.1 基本安装 2.1.1 下载Redis 2.1.2 解压并安装Redis 2.1.3 编译Redis 2.1.3 配置config文件 2.1.4 配置redis服务 2.1.5 关闭redis服务 2.2 Docker安装 2.2.1 拉取镜像 2.2.2 查看镜像 2.2.2 创建挂载目录 2.2.3 创建配置文件 2.2.4 创建容器…

微信商家费率0.2怎么申请

不管是微信还是支付宝&#xff0c;商户最低的收款手续费率可以达到0.2%费率。一般我们普通商户的收款费率一般在0.6左右&#xff0c;当然也有使用0.3的&#xff0c;也就是1万元的费率是30-60块钱&#xff0c;对于一些流水比较大的商家来说&#xff0c;确实很有必要把这个手续费…

ThunderSearch(闪电搜索器)_网络空间搜索引擎工具_信息收集

文章目录 ThunderSearch简介1 项目地址2 使用方式2.1 配置文件config.json说明2.2 构建和运行 3 使用式例 ThunderSearch简介 ThunderSearch&#xff08;闪电搜索器&#xff09;是一款使用多个(【支持Fofa、Shodan、Hunter、Zoomeye、360Quake网络空间搜索引擎】网络空间搜索引…

Vue爱好者必看!九款常用UI组件库,助力项目快速搭建!

Vue通常用于构建用户界面和单页应用程序。产品开发者可以从简单的组件开始创建&#xff0c;并逐渐衍生出更复杂的前端平台。通过合理应用UI组件库&#xff0c;设计师可以快速提高整体设计效率&#xff0c;更好地优化产品从外观结构到交互体验。本节将盘点9个常用的VueUI组件库。…

Linux与Bash 编程——Linux文件处理命令-L1

目录&#xff1a; linux系统与shell环境准备 Linux系统简介操作系统简史Linux的发行版&#xff1a;Linux与Windows比较&#xff1a;Linux安装安装包下载Linux的访问方式远程登录方式远程登录软件&#xff1a;mobaxterm的使用&#xff1a;使用电脑命令行连接&#xff1a;sshd的…