【JavaEE】初识线程


一、简述进程

认识线程之前我们应该去学习一下“进程" 的概念,我们可以把一个运行起来的程序称之为进程,进程的调度,进程的管理是由我们的操作系统来管理的,创建一个进程,操作系统会为每一个进程创建一个 PCB,并为进程在内存中开辟一块运行空间 ,然后把这个 PCB 加入到链表中。

进程的调度是为了解决,处理多进程运行的机制,CPU 按照并发的方式执行进程,在进程之间高速切换,看起来就是多进程同时运行。为了描述控制进程的运行,系统中存放进程的管理和控制信息的数据结构称为进程控制块(PCB Process Control Block),它是进程实体的一部分,是操作系统中最重要的记录性数据结构。它是进程管理和控制的最重要的数据结构,每一个进程均有一个PCB,PCB 记载了进程的优先级,进程的状态,进程的上下文,进程的记账信息等等。

进程的概念就是为了能够实现,多任务,并发执行(”同时执行“)的机制。

能否实现多进程并发执行,需要看操作系统是否能够支持操作,执行的效率这个是考验CPU 的性能。

想要学习更多进程知识的朋友可以观看博主的上一篇博客——【JavaEE】浅识进程_保护小周ღ的博客


二、什么是线程

进程是操作系统分配资源的基本单位,无论是创建,还是调度都是非常麻烦的,进程与进程之间的独立性是较高的,多进程协同维护同一个程序,对资源的消耗是非常大的。

举个例子:我们的QQ 当我们与别人打qq电话的时候,还可以接收qq 消息,并且qq空间同一时刻也接收了你铁哥们发的动态,做个假设,这三个功能是不是可以看作是三个进程,他们可以相互独立的运行,不受其他进程的影响,进程之间又有着某种关联,可以相互通信,他们共同维护了 qq 这个应用。

那么这个假设的例子:我们运行qq , 操作系统此时要给三个进程分配系统资源,开辟内存空间,而且还要花费精力保证进程之间的通信、关联性,是不是很复杂~此时的复杂主要体现在系统资源的分配上。


2.1 线程的概念

线程是建立在进程的基础之上的,可以看作是轻量级的进程,一个进程可以包含一个或者多个线程,同一个进程下的线程之间都是独立且可以调度执行的“执行流”,也是并发执行的,这些线程之间共用父进程的系统资源。

举个例子:


根据以上实例:

  1. 线程是建立在进程的基础上的,进程包含线程

  1. 同一个进程内部,多个线程之间,共用一份系统提供给进程的资源(内存空间,资源共享)

  1. 启动进程后(一般是会创建一个线程,例如 JAVA ,C/C++ 中的main() 函数,程序从main()函数开始执行,操作系统为main() 函数开辟栈帧,然后CPU 的寄存器处理、维护栈帧),需要系统花时间,花精力去分配系统资源,进程创建完毕后,无论当中有多少个线程,站在进程的角度上都不需要再去申请系统资源了,线程之间共用一份进程资源。

  1. 进程是系统分配资源的基本单位。

  1. 操作系统真正调度的是线程,线程是操作系统调度运行的基本单位。

  1. 进程之间相互独立,同一个进程之下,线程之间共享进程资源,此时如果其中一个线程崩溃有可能会对其他线程造成影响,甚至是崩溃。

  1. 一个进程中的线程数应当设计合理,线程之间也是并发执行,而CPU 的核心处理器是有限的,如果同一个进程下的线程过多,虽然在系统资源的分配上只需要执行一次,但是 CPU 需要并发处理的数量过多的话,反而会使得线程的执行的效率变低(CPU高速来回切换的处理线程)。

上文博主假设的qq 例子,应该是一个进程,然后不同的功能交由多线程来执行,功能之间可以独立并发执行,共同维护我们的qq。


三、 Java创建线程

Java 学习过程中主要是偏向于多线程开发,那么接下来学习的是如何用Java 代码来创建一个线程。

3.1 继承Thread 类

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

API : Application Programing linerface

给你一个软件,你能对他干什么,基于它提供的这些功能,就可以写一些代码,然后封装在一起,方便别人使用。

举个例子:

张三肚子饿了,想吃猪脚饭,自己做吧,首先得买猪脚,买调料,洗猪脚,切割,起锅烧油……想想都麻烦,于是张三打消了自己做饭的念头,于是前往楼下小吃店购买猪脚饭,张三到店之后,老板娘给张三一张纸,让他写一写自己想吃啥。

什么意思?我们想吃猪脚饭,不需要知道猪脚饭是怎么做的,我们只需要知道哪里有卖猪脚饭得地方即可

我们想创建一个线程,首先得找到 ”饭店“,这个饭店就是操作系统对创造线程操作封装的API ,然后JAVA 把 操作系统提供的 API 进一步的处理,封装成 Thread 类,我们不需要知道 系统是怎么创建一个线程的,只需要知道 Thread 类,可以吃到 "猪脚饭”。

代码实现:

class MyThread extends Thread {
    // MyThread 类继承 Thread 类,创建一个线程类
    // 并重写 Thread 类的 run() 方法
    // 此时MyThread 线程类的 run() 方法相当于 主线程中的 main() 方法
    @Override
    public void run() {

        while (true) {
            try {
                Thread.sleep(1000); //线程休眠,1000 = 1 秒
                System.out.println("t 线程 执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        // 创建 MyThread 线程类实例化对象
        Thread t = new MyThread();
        // start 创建是新的线程并启动执行,调用run() 方法
        // 创建线程默认就会执行线程的 run 方法
        // 不调用 start() 方法线程不会启动
        t.start();

        while (true) {
            try {
                Thread.sleep(1000); //线程休眠,1000 = 1 秒
                System.out.println("Main主线程 执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

线程不调用 start() 方法线程不会启动

调用start() 方法会从系统中创建一个线程,新的线程会执行 run 方法,run 方法式线程的入口方法,类型于主线程的 main() 方法。

启动线程之后,线程就会进入就绪状态,随时准备被系统调度,CPU 执行。

两个线程中分别设置了死循环,打印线程执行,可以出看出控制台两个线程都可以打印数据,也就是说两个线程之间宏观上是并发执行的,线程执行的顺序是无序的。

MyThread 线程类的 run() 方法相当于 主线程中的 main() 方法,都是描述线程的入口。

疑问点:为什么调用 start() 方法会自动执行 run() 方法?

因为类Thread中的start方法中,调用了Thread中的run方法。
MyThread继承了Tread类,在MyThread 中重写run方法,就会覆盖掉Thread中的run方法,子类重写父类方法,优先调用子类重写后的方法,如果子类没有重写父类方法,默认执行的是父类的 run 方法,所以子类调用start方法后,实现的是自己的run方法体里面的代码。

3.2 实现 Runnable 接口

  1. 自定义一个类使其实现 Runnable 接口

class MyThread2 implements Runnable { //实现 Runnable 接口
    @Override
    public void run() { //重写接口里面的 run 方法
       //线程运行的代码
       while (true) {
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("线程t执行");
       }
    }
}
  1. 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.

public class Demo2 {
    public static void main(String[] args) {
        //创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.
        Thread t = new Thread(new MyThread2());
        t.start();// 启动线程

        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主线程 main 执行");
        }
    }
}

这是 Thread 线程类提供的有参构造方法,可以看出里面是 Runnable 接口的引用来接收,我们自定义的线程类由于实现了 Runnable 接口,此时发生向上转型,父接口引用接收子类对象,由于子类重写了 Runnable 接口的 run 方法,所以接口引用可以直接调用子类重写后的 run 方法,如果在 父类想调用子类其他独有的成员变量或者是方法,就需要 向下转型(强制类型转换)。

然后线程对象 t 调用 start() 方法启动线程然后调用 run() 方法。

采用实现接口方式创建一个线程 与 继承 Threard 类 创建一个线程 最终的结果是没什么区别的。


3.3 lambda 表达式创建线程

Thread t = new Thread(() -> {
      System.out.println("使用匿名类创建 Thread 子类对象");
});

t.start();

我们常常在通过创建 Thread 对象的时候,通过创建匿名内部类重写 run() 方法。

这里创建的匿名内部类

lambda 表达式的本质就匿名函数。

语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。

有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以利用 lambda表达式的接口快速指向一个已经被实现的方法。

public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread( () -> {
           while (true) {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("线程 T执行");
           }
        });

        t.start(); //启动

        while (true) {
            Thread.sleep(1000);
            System.out.println("主线程Main执行");
        }
    }
}

至此,进程的基本认识博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。

本期收录于博主的专栏——JavaEE,适用于编程初学者,感兴趣的朋友们可以订阅,查看其它“JavaEE基础知识”。

下期预告:Thread 类 以及 常见方法

感谢每一个观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘

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

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

相关文章

C++之深浅拷贝

一、浅拷贝 我们看下以下代码 Test.h 文件 #pragma once #include<iostream> using namespace std; class Student { public:Student(){}~Student(){if (m_Id ! nullptr){delete m_Id;m_Id nullptr;}}Student(int id, string strName){m_Id new int[id];m_strName s…

字符函数和字符串函数(上)-C语言详解

CSDN的各位友友们你们好,今天千泽为大家带来的是C语言中字符函数和字符串函数的详解,掌握了这些内容能够让我们更加灵活的运用字符串,接下来让我们一起走进今天的内容吧!写这篇文章需要在cplusplus.com上大量截图,十分不易!如果对您有帮助的话希望能够得到您的支持和帮助,我会持…

信号处理-小波变换4-DWT离散小波变换概念及离散小波变换实现滤波

连续小波变换的适用场景&#xff1a;能够获取某一段信号的瞬时信息、时频信息 缺点&#xff1a;计算量大&#xff0c;无法进行数据压缩- 针对连续小波存在的缺点提出离散小波变换 离散小波变换 离散小波变换 分解过程&#xff1a;&#xff08;离散2进正交&#xff09; cD1: …

数据结构与算法——栈和队列<也不过如此>

&#x1f3c6;作者主页&#xff1a;king&南星 &#x1f384;专栏链接&#xff1a;数据结构 &#x1f3c5;文章目录一、&#x1f947;栈1、&#x1f948;概念理解2、&#x1f948;链表头插头删实现栈1、&#x1f949;预备准备2、&#x1f949;创建结点函数3、&#x1f949;遍…

SPI读写SD卡速度有多快?

SD卡是一个嵌入式中非常常用的外设&#xff0c;可以用于存储一些大容量的数据。但用单片机读写SD卡速度一般都有限&#xff08;对于高速SD卡&#xff0c;主要是受限于单片机本身的接口速度&#xff09;&#xff0c;在高速、实时数据存储时可能会有影响。但具体速度可以达到多少…

vue2+高德地图web端开发使用

创建vue2项目我们创建一个vue2项目&#xff0c;创建vue2项目就不用再多说了吧&#xff0c;使用“vue create 项目名 ”创建即可注册高德地图高德地图官网地址&#xff1a;https://lbs.amap.com/如果是第一次使用&#xff0c;点击注册然后进入我们的控制台注册完之后进入控制台&…

<Linux>计算机体系结构和操作系统

计算机体系结构(冯 • 诺依曼体系)和操作系统&#xff08;Operator System&#xff09; 文章目录计算机体系结构(冯 • 诺依曼体系)和操作系统&#xff08;Operator System&#xff09;一、冯 • 诺依曼体系结构1.存储器&#xff08;内存&#xff09;2.运算器和控制器&#xff…

系统重装漏洞

zzcms系统重装漏洞 一、配置zzcms环境 1. 使用小皮搭建zzcms框架 2. 安装zzcms 按照下面的操作进行,傻瓜式操作即可 3. 打开网站 二、漏洞利用 在访问install目录的默认文件后,会出现zzcms安装向导 http://www.zzcms.com/install/index.php 但是会显示 “安装向导…

HTTPS,SSL(对称加密和非对称加密详解)

上一篇博客&#xff08;HTTP详解_徐憨憨&#xff01;的博客-CSDN博客&#xff09;详细讲解了关于HTTP的知识&#xff0c;了解到HTTP协议下的数据传输是一种明文传输&#xff0c;既然是明文传输&#xff0c;可能导致在传输过程中出现一些被篡改的情况&#xff0c;此时就需要对所…

MATLAB | 给热图整点花哨操作(三角,树状图,分组图)

前段时间写的特殊热图绘制函数迎来大更新&#xff0c;基础使用教程可以看看这一篇&#xff1a; https://slandarer.blog.csdn.net/article/details/129292679 原本的绘图代码几乎完全不变&#xff0c;主要是增添了很多新的功能&#xff01;&#xff01;&#xff01; 工具函数完…

【链表OJ题(六)】链表分割

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录链表OJ题(六)1. 链表…

燕山大学-面向对象程序设计实验-实验三 类和对象—构造函数与析构函数-实验报告

CSDN的各位友友们你们好,今天千泽为大家带来的是燕山大学-面向对象程序设计实验-实验三 类和对象—构造函数与析构函数,接下来让我们一起进入c的神奇小世界吧,相信看完你也能写出自己的 实验报告!如果对您有帮助的话希望能够得到您的支持和帮助,我会持续更新的!&#x1f680;3.…

硬刚ChatGPT!文心一言能否为百度止颓?

引言&#xff1a;近年来&#xff0c;人工智能领域的发展突飞猛进&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;方面。OpenAI的ChatGPT无疑是这个领域的佼佼者[1]。那么&#xff0c;面对这样一款高度智能的AI模型&#xff0c;国内市场上是否有相应的产品能与之抗…

你是真的“C”——指针进阶知识分享【上篇】

你是真的“C”——指针进阶知识分享【上篇】&#x1f60e;前言&#x1f64c;指针初阶必备小知识~&#x1f60a;一. 字符指针&#x1f60a;二. 指针数组&#x1f60a;三、数组指针&#x1f60a;数组指针的定义&#x1f618;四、 &数组名VS数组名&#x1f60a;总结撒花&#…

【K8S系列】从零开始学习 k8s:入门指南(二)

目录 序言 前情提要&#xff1a; 4.K8S架构 4.1 声明式系统VS命令式系统 4.2 k8s-声明式系统 4.2.1 声明方式-yaml 4.3 Kubernetes的基本概念 1.集群 2.节点 3.容器 4.Pod 5.Service 6.Deployment 问题&#xff1a; 4.4 K8S核心组件 4.4.1 kube-apiserver 4.4…

【Linux学习】进程间通信——system V(共享内存 | 消息队列 | 信号量)

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《Linux学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 进程间通信——共享内存 | 消息队列 | 信号量&#x1f3c0;共享内存⚽系统调用shmgetkey值⚽系统…

提升Python代码性能的六个技巧

文章目录前言为什么要写本文&#xff1f;1、代码性能检测1.1、使用 timeit 库1.2、使用 memory_profiler 库1.3、使用 line_profiler 库2、使用内置函数和库3、使用内插字符串 f-string4、使用列表推导式5、使用 lru_cache 装饰器缓存数据6、针对循环结构的优化7、选择合适算法…

【WEB前端进阶之路】 HTML 全路线学习知识点梳理(下)

前言 本文是HTML零基础小白学习系列的第三篇文章&#xff0c;点此阅读 上一篇文章 文章目录前言十五.HTML布局1.使用div元素添加网页布局2.使用table元素添加网页布局十六.HTML表单和输入1.文本域2.密码字段3.单选按钮4.复选框5.提交按钮十七.HTML框架1.iframe语法2.iframe设置…

Windows电脑密码忘记解决方法

目录 背景 方法一 方法二 方法三 方法四 方法五 背景 个人电脑忘记了密码&#xff0c;无法登录用户界面。 方法一 1. 开机时常按 F11&#xff0c;如果是Win10一下系统&#xff0c;就常按 F8&#xff0c;知道出现一下图状 2. 选择疑难解答&#xff0c;再选择高级选项 3.…

Tomcat前端页面部署

一&#xff0c;Tomcat的安装1.Tomcat是什么Tomcat是一个HTTP服务器&#xff0c;HTTP协议是HTTP客户端和HTTP服务器之间交换数据的格式&#xff0c;我们可以通过ajax和Java Socket分别构造HTTP客户端&#xff0c;同时HTTP服务器也可以通过Java Socket来实现&#xff0c;而Tomcat…