线程的概念和创建【javaee初阶】

news2024/12/28 20:44:20

目录

一、认识线程 

二、多线程程序

2.1 实现Java多线程程序

方法1 继承 Thread ,重写run

 方法2 实现 Runnable 接口

方法3  匿名内部类创建 Thread 子类对象

 方法4 匿名内部类创建 Runnable 子类对象

 方法5 lambda 表达式创建 Runnable 子类对象

三、多线程的优点

四、多线程的使用场景

sleep方法

一、认识线程 

        一个线程就是一个 "执行流". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 "同时" 执行着多份代码.

首先我们需要知道,为啥要有多个进程呢?

           多进程编程已经可以解决很多并发编程的问题了,CPU的很多资源也可以调度起来了。但是,在资源分配和回收上,多进程还是有很多短板。主要有以下三点:

        1.创建一个进程,开销比较大。

        2.销毁一个进程,开销比较大。

        3.调度一个进程,开销也比较大。

        那是因为我们要 并发编程(CPU 单个核心已经发展到极致了,要想提升算力,就得使用多个核心) 

        引入 并发编程,最大的目地就是为了能够充分的利用好 CPU 的多核资源(如果在写代码的时候,不去处理一下,默认只会用到一个核心,造成资源浪费) 

创建/销毁进程,本身就是一个比较低效的操作:

  1. 创建 PCB
  2. 分配系统资源(尤其是 内存资源)
  3. 把 PCB 加入到内核的双向链表中

其中,分配资源 就已经是特别需要消耗时间了(在系统内核资源管理模块,需要进行一系列的遍历操作) 。 那么进程消耗资源多,速度慢(创建,销毁,调度),资源分配回收效率低,于是就出现了线程(轻量级进程)解决并发编程的前提下,让创建,销毁,调度的速度快一点,主要是把申请资源/释放资源的操作省下来。

        例如:一个服务器需要同一时刻为多位客户端提供服务。此时,就需要使用到 并发编程 。典型的做法就是  每个客户端给他分配一个进程,提供一对一的服务。客户端来了,就需要创建进程;客户端走了,就需要销毁进程。如果客户端来来回回很多,就需要 频繁的创建/销毁进程,这样使用进程就会比较低效

        由于频繁的创建/销毁进程,是一件比较低效的事情。所以,就引入了 "线程"这一概念,因此线程 也叫做 "轻量级进程"。

说明:

  1. 一个线程其实是包含在进程之中的(一个进程里面可以有多个线程)
  2. 每个线程也有自己的 PCB (所以 一个进程里面可能对应多个 PCB) 
  3. 同一个进程的多个线程之间,共享同一份系统资源(这就意味着 新创建的线程,不必给他分配系统资源,只需要复用之前的即可,即 上述的分配资源的操作,就不需要再进行了)

因此,创建线程只需要做到:

  • 创建 PCB
  • 把 PCB 加入到内核的链表中

这就是 线程相对于进程做出的重大改进,也是进程更 "轻量" 的原因!!!

列一个小例子来帮助大家理解:

张三家里是开厂子的,最近几年来生意非常好,需求的订单比较多,于是他准备扩建一下

现在有两种方案:

  1. 再次建造一个相同的工厂
  2. 在原来的工厂里面 新增加一条生产线

两个方案都可以以相同的效率生产产品,很明显,方案1所需要的成本更高

如果可以把进程就可以看作是 工厂,那么线程就可以看成是 生产线~

方案1表示多进程实现并发编程,方案2表示多线程实现并发编程~

使用多线程是能够提高效率,前提是多核资源必须是要充分的~

如果 随着线程数量的增加,CPU 核心都被吃满了,那么此时再继续增加线程,对时间效率就已经没有意义了。这个时候速度不会进一步增加,反而会因此增加额外调度的成本 


总结:

  1. 线程,是包含在进程内部的 "逻辑执行流"(线程可以执行一段单独的代码,多个线程之间 是并发执行的)
  2. 操作系统进行调度的时候,其实是以 "线程为单位" 来进行调度的,换句话来说,系统内核不认 进程/线程,只认PCB(一个线程对应一个 PCB,一个进程对应 一个或多个 PCB)
  3. 进程里的线程的数量不可以无限增加,效率不会越来越高
  4. 创建线程的开销要比创建进程的开销要小,销毁线程开销要比销毁进程的开销要小
  5. 进程间是独立的,每个进程都有独立的虚拟地址空间,一个进程崩溃不会影响其余的进程;但是在同一个进程中,多个线程是共用一块资源,一个线程崩溃,这个进程中的所有线程都会崩溃
  6. 进程之间有隔离性,线程之间没有隔离性
  7. 进程是操作系统中 资源分配 的基本单位,线程是操作系统中 调度执行 的基本单位~

 1.1 进程和线程的区别

  1. 进程是包含线程的. 每个进程至少有一个线程存在,即主线程。
  2. 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.
  3. 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
  4. 线程模型,天然就是资源共享的,多个线程抢同一个资源,非常容易触发线程安全问题;进程模型,天然就是资源隔离的,不容易触发。进行进程间通信的时候,多个进程访问同一个资源,就可能出现问题

  5. 进程的上下文切换速度比较慢,而线程的上下文切换速度比较快。

  6. 进程都拥有自己独立的虚拟地址空间,有多个进程时,其中一个进程崩溃了并不会影响其他进程。但是线程是多个线程共用一个内存空间,当一个线程抛异常,如果处理不好很可能会把整个进程都给带走了,其他线程也就挂了

注意:

  1. 增加线程数量也不是一直可以提高速度的,CPU核心数量是有限的,线程数量太多开销反而浪费在线程调度上
  2. 系统创建线程也是要消耗资源的,虽然比进程轻量但也不是0,创建太多线程会导致资源耗尽,导致别的进程用不了
  3. 同一个进程里的多个线程之间共用了进程的同一份资源(内存和文件描述符表),只有第一个线程启动时开销是比较大的,后续线程就省事了

Java操作多线程最核心的Thread类,不需要导其他包。java.lang包下最基本的类。

二、多线程程序

 

即使是一个最简单的 hello world,其实在运行的时候也涉及到 "线程" 了。 

        运行这个程序,操作系统就会创建一个 Java进程,在这个 Java进程 里就会有一个线程(主线程)调用 main方法。虽然上述代码中,我们没有手动的创建其他线程,但是 Java进程 在运行的时候,内部也会创建出线程。(一个进程里面至少有一个线程)  

        注意:在谈到多进程的时候,会经常谈到 "父进程" 和 "子进程",如果在 A进程 里面创建了 B进程,那么A 是 B 的父进程,B 是 A 的子进程。但是,在多线程里,没有 "父线程" 和 "子线程" 的说法,因为我们认为 线程之间的地位是对等的~ 

2.1 实现Java多线程程序

创建线程的方法:

方法1 继承 Thread ,重写run

Java中创建线程,离不开一个关键的类 —— Thread;

1) 继承 Thread 来创建一个线程类

    class MyThread extends Thread{
        @Override
        public void run(){
            System.out.println("hello thread");
        }
    }

 2) 创建 MyThread 类的实例   

MyThread t = new MyThread();

3) 调用 start 方法创建启动线程,新的线程负责执行t.run(); 

 class MyTread extends Thread {
    @Override
    public void run() { 
        while(true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
 
    }
}
public class Main{
    public static void main(String[] args) { 
        Thread t = new MyTread();
        t.start();
        }
    }
}


 方法2 实现 Runnable 接口

1) 实现 Runnable 接口

class MyRunnable implements Runnable{
    @Override
    public void run(){
        System.out.println("hello thread");
    }
}

2) 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.

Thread t = new Thread(new MyRunnable());

3) 调用 start 方法创建启动线程,新的线程负责执行t.run(); 

 t.start(); // 线程开始运行
class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } 
    }
}
public class Main{
    public static void main(String[] args) {
        //创建线程 
        Thread t = new Thread(new MyRunnable());
        t.start(); 
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

分析:

 此处创建的 Runnable,相当于是定义了一个 "任务"(代码要做什么),

还是需要 Thread实例,把任务交给 Thread,

还是需要 Thread.start 来创建具体的线程


说明:

这个写法,线程和任务是分离开的,可以更好的解耦合,"高内聚 低耦合",因此使用实现 Runnable接口的方法更好

把任务内容 和 线程 本身分离开了,即 任务的内容和线程的关系不大~

假设这个任务不想通过多线程的方式执行了,想通过别的方式来执行,这个时候代码改动也不大 


方法3  匿名内部类创建 Thread 子类对象

我们也可以仍然继承 Thread类,但是不在是显式继承,而是使用 "匿名内部类" 来创建线程~

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread (){
            @Override
             public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t.start();
    }
} 

运行结果:

 

分析:

 红色框里面的内容,创建了一个匿名内部类(没有名字),这个匿名内部类是 Thread 的子类,同时前面的 new 关键字,就会给这个匿名内部类创建出了一个实例

创建一个Thread的子类(子类没有名字,所以才叫匿名)
创建了子类的实例,并且让t引用指向该实例


 方法4 匿名内部类创建 Runnable 子类对象

Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

 运行结果:

 

 和方法二本质相同,只不过是把实现Runnable任务交给匿名内部类的语法

此处创建了一个类,实现Runnable,同时创建了类的实例并且传给Thread的构造方法


 方法5 lambda 表达式创建 Runnable 子类对象

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(() ->{
            while(true){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

 运行结果:

 

说明:

使用 lambda表达式,其实是更简单的写法,也是推荐写法;

形如 lambda表达式这样的,能够简化代码编写的语法规则,称为 "语法糖"~  

理解run和start:

        重写的 run方法 —— 先把新员工的任务准备好;

        Thread t = new MyThread(); //  招聘了一个新员工 t,把任务交给他(但是还没有开始干活);

        t.start(); —— 开始干活

即:使用 new 创建线程对象,线程并没有被创建,而是仅仅创建了一个线程对象,运行 start 方法 时才会创建线程,并执行 run 方法

在 start 之前,线程只是准备好了,并没有真正的被创建出来,执行了 start方法,才真正在操作系统中创建了线程~

Thread 实例是 Java 中对于线程的表示,实际上要想正真跑起来,还需要操作系统里面的线程~

创建好了 Thread,此时操作系统里面还没有线程,直到调用 start方法,操作系统才真的创建了线程(创建 PCB,并且把 PCB 加入到链表里),并且进行执行起来 

直接调用 run方法,并没有创建新的线程,而只是在之前的线程中,执行了 run方法里面的内容;使用 start方法,则是创建了新的线程,新的线程里面会调用 run方法,新线程和旧线程是并发执行的关系~

三、多线程的优点

单个线程,串行的,完成 20 亿次自增~

package thread;
 
public class Demo6 {
    private static final long count = 20_0000_0000;
    private static void serial() {
        //serial 是 "串行" 的意思
        //需要把方法执行的时间给记录下来
        //记录当前的毫秒计时间戳
        long begin = System.currentTimeMillis(); 
        int a = 0;
        for(long i = 0; i < count; i++) {
            a++;
        } 
        a = 0;
        for(long i = 0; i < count;i++) {
            a++;
        }  
        long end = System.currentTimeMillis();
        System.out.println("单线程消耗的时间是 :" + (end-begin) + "毫秒");
    } 
    public static void main(String[] args) {
        serial();
    }
}

运行结果:

 多运行几次,取其平均值,所得的结果大概是: 1070毫秒 


多个线程,并发的,完成 20 亿次自增~

 private static void concurrency() {
        //concurrency 的意思是 "并发"
        long begin = System.currentTimeMillis();
        Thread t1 = new Thread(() ->{
            int a = 0;
            for(long i = 0; i < count; i++) {
                a++;
            }
        }); 
        Thread t2 = new Thread(() ->{
            int a = 0;
            for(long i = 0; i < count; i++) {
                a++;
            }
        }); 
        t1.start();
        t2.start(); 
        long end = System.currentTimeMillis();
        System.out.println("多线程并发执行的时间:" + (end-begin) + "毫秒");
    }

这个代码,涉及到三个线程:t1、t2、main ,三个线程都是并发执行的。即 t1、t2会开始执行,同时,可能不等t1、t2执行完,main线程就结束了,于是就结束计时。

此处的计时,是为了衡量 t1 和 t2 的执行时间,所以正确的做法应该是等到 t1 和 t2 都执行完,才停止计时。

所以在 创建线程的时候,还需要使用 jion方法,jion方法是等待线程结束(等待线程把自己的 run方法执行完)。所以还需要在 t1 和 t2 创建线程后 加上: 

 并此时可以在 mian方法中调用 concurrency() 方法~

运行结果:

并同时执行多次 取其均值,我们可以发现,并发执行的时间的平均值 在 600 毫秒左右~ 

所以,相比之下,我们可以知道,多线程的效率确实是提高不少。

当然,如果在任务量不大的情况下,可能多线程并不会比单线程有太大的优势。


四、多线程的使用场景

(1)在 CPU 密集型场景~

        代码中的大部分工作,都是在使用 CPU 进行运算(如 上面的反复 ++ 运算),此时使用 多线程 就可以更好的利用 CPU 多核计算资源,从而提高效率~

(2)在 IO 密集型场景~

  • I : input 输入
  • O : output 输出

如 读写硬盘、读写网卡......这些都算 IO,需要花很大的时间等待

像这些 IO 操作,都是几乎不消耗 CPU 就能快速的完成读写数据的操作,既然 CPU 在摸鱼,就可以找点活干,可以使用多线程,避免 CPU 过于闲置~ 

这就好比去食堂打饭,但是人多要排队,排队的过程就是等待(类似于 等待IO结束),于是 就可以做别的事(刷手机)

 怎么样观察线程的详细情况 

  那么,此时的运行结果是:

        明明是先执行了线程,后打印的"hello main",但是 为什么结果却是 先打印出来 "hello main",后打印的"hello thread" 呢?


每个线程都是独立的执行流!换句话说,main对应了一个执行流,MyThread对应了另一个执行流,这两个执行流之间是 并发 的关系~
此时两个线程执行的先后顺序,取决于操作系统 调度器 的具体实现~
程序猿可以把这里的调度规则 简单的视为 "随机调度",这个是改变不了的~

如果是想要控制哪个线程先执行,最多是让某个线程先等待,让另一个线程执行完了再执行~

所以,当程序运行的时候,先看到哪一个被执行的顺序 是不确定的,

虽然 可以在这里运行了许多次,先打印出来的是"hello main",但是顺序仍然是不可确定的,大概率是受到了创建线程自身的开销影响的~ 

当执行结果中出现了这一句话,就说明 进程已经结束了,并且退出码是 0:

  操作系统中用 进程的退出码 来表示进程的运行结果:

 使用0表示进程执行完关闭,结果正确;使用 非0 表示进程执行完关闭,结果不正确;还有一种情况是 main还没有返回,程序就崩溃,此时返回的值很可能是一个随机值~ 

可以在任务管理器中 看见Java进程的情况(需要把死循环的代码运行起来,不然嗖的一下就没了):

当然,此时是看不到 Java线程的,需要借助其他的工具。 在 JDK 里,提供了一个 jconsole 这样的工具,可以看到 Java进程里面的线程的详情~

  运行 jconsole 之后,就可以看到 线程的情况了:


sleep方法

如果想要线程来适当的 "休息" 一下,为了方便观察,不要让刚刚的死循环代码 打印 "hello main" 和 "hello thread" 打印的太多太快,我们可以用 sleep 来进行操作~

sleep 是 "休眠" 操作,指定让线程摸一会儿鱼,不要上 CPU 上干活,参数单位是 毫秒~

使用 Thread.sleep 的方式进行休眠,sleep 是 Thread 的静态成员方法,直接通过 类名.方法名 的方式调用~

时间单位的换算:

1秒 = 1000毫秒,1毫秒 = 1000微秒,1微秒 = 1000纳秒,1纳秒 = 1000皮秒~

秒(s)、毫秒(ms)、微秒(us)、纳秒(ns)、皮秒(ps)~ 

由于计算机算得快,所以常用的单位是:ms、us、ns这几个单位~ 

 Interrupted 中断!!!

sleep(1000)就是要休眠 1000毫秒,但是 在休眠过程中,可能有一点点意外 把线程给提前唤醒 —— 该异常唤醒的~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kamiya丨Kamiya艾美捷小鼠高敏CRP ELISA说明书

Kamiya艾美捷小鼠高敏CRP ELISA预期用途&#xff1a; 小鼠高敏CRP ELISA是一种高灵敏度的双位点酶联免疫分析&#xff08;ELISA&#xff09;定量测定小鼠生物样品中的C-反应蛋白&#xff08;CRP&#xff09;。仅供研究使用。 引言 急性期蛋白质是血浆蛋白质&#xff0c;其在感…

交互作用的深入剖析

1.原理说明 交互作用(Interaction effect)指两个或多个因素&#xff08;比如A因素和B因素&#xff09;对于某项&#xff08;比如身高&#xff09;的影响时&#xff0c;A因素和B因素各个水平之间是否存在着相互作用关系。比如A因素为性别&#xff0c;B因素为区域&#xff0c;男…

Springboot+vue+java幼儿园管理系统

开发语言&#xff1a;Java 框架&#xff1a;springboot 前端框架:vue.js JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat8 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/idea 目 录 第一章 绪 论 1 1.1背景及意义 1…

Android Qcom Sensor架构学习

Android Sensor Brief Flow Android Sensor Specific Flow ADSP SSC ADSP.VT.5.4.1/adsp_proc/ssc_api/pb/ ADSP.VT.5.4.1/adsp_proc/ssc/sensors ADSP.VT.5.4.1/adsp_proc/ssc/frameworksADSP Framework初始化的时候首先通过load image并初始化的静态加载方式register_static…

数据结构期末刷题

写在前面 此篇文章是在网络上搜集的题目&#xff0c;每一题都可能出现错误&#xff0c;如果各位大佬发现了错误&#xff0c;请在评论区回复一下&#xff0c;看到了就会改 树 根据遍历序列&#xff0c;画树&#xff0c;求另外一种遍历序列 以下中序遍历简称为中序&#xff0…

电路设计 > eMMC应用和PCB layout布局布线参考设计

目录 eMMC介绍 eMMC信号大体介绍 EMMC4.5和5.0封装和兼容性对比 EMMC5.0和5.1封装和兼容性对比 PCB Layout建议 PCB layout参考设计 参考设计1 参考设计2 参考设计3 参考设计4 参考设计5​​​​​​​ eMMC介绍 主要针对现在主流的eMMC5.0以及以上版本。 eMMC信号…

centos 7 安装node-red

一、安装nodejs https://nodejs.org/en/download/ 将其上传到服务器/usr/local/src目录下 下载之后是xz文件&#xff0c;解压xz文件 xz -d node-v16.18.1-linux-x64.tar.xz 解压之后会多一个tar文件&#xff0c;解压tar文件 tar -vxf node-v16.18.1-linux-x64.tar mkdir …

Navicat远程连接MySQL服务器

文章目录一、准备二、配置Navicat允许远程连接MySQL数据库1、使用Navicat直接连接MySQL2、使用 Navicat 通过 SSH 远程登录后再本地方式连接 MySQL3、查看连接为什么使用ssh登录1.便捷性Navicat:数据库可视化工具 一、准备 一台开启 SSH 登录的 Ubuntu 服务器 或 已开启远程登…

PyTorch学习笔记-神经网络Torch.NN基本骨架的使用及卷积原理

1. torch.nn.Module介绍 torch.nn 能够帮助我们更优雅地训练神经网络&#xff0c;使神经网络代码更加简洁和灵活。官方文档&#xff1a;Torch.NN。 在文档中可以看到第一块内容叫做 Container&#xff08;容器&#xff09;&#xff0c;这就相当于神经网络的骨架&#xff0c;C…

U2Net——U-Net套U-Net——套娃式图像分割算法

U2Net1 相关参考2 U2−NetU^2-NetU2−Net 网络结构3 网络代码和测试1 相关参考 论文名称&#xff1a; U2-Net: Goging Deeper with Nested U-Structure for Salient Object Detetion 论文地址&#xff1a; https://arxiv.org/abs/2005.09007 官方源码&#xff1a; https://git…

机器视觉_工业相机及相关配件选型

文章目录工业相机一、 概述二、 相机参数1. 传感器芯片1.1. CCD&CMOS1.2. CCD1.3. CMOS1.4. 靶面1.5. 传感器芯片选型2. 黑白or彩色3. 帧数⭐4. 接口类型4.1. POE供电三、相关硬件1. 镜头1.1. 焦距2. 光源3. 控制器4. 工控机5. 线缆6. 交换机四、参考工业相机 一、 概述 …

基于FPGA的呼叫设备verilog开发

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 目录 一、理论基础 二、核心程序 三、测试结果 一、理论基础 随着集成电路技术的发展&#xff0c;电子设计自动化&#xff08;EDA&#xff09;逐渐成为重要的设计手段&#xff0c;已经广泛应用于模拟与数字…

Vite+Vue3+TS项目创建及基本环境搭建

1.vite项目搭建 可以按照vite官网操作&#xff1a;https://cn.vitejs.dev/guide/features.html#typescript npm create vitelatest自定义template模板 vscode-文件-首选项-配置用户代码片段-vue.json 添加如下代码即可快速创建vue模板 {"template": {"pref…

做好供应商关系管理,让企业采购交易更简单

大家都知道&#xff0c;企业想要采购到物美价廉的产品与服务&#xff0c;就必须做好相应的供应商管理工作。而其中供应商关系管理是采购过程中至关重要的环节&#xff0c;也是能让企业有效地与供应商之间保持良好关系&#xff0c;这不仅有利于促进采供双方共赢&#xff0c;而且…

Netty入门--传统IO与NIO详解

文章目录IO模型传统阻塞的IO模型--BIOClient端案例Server端案例NIO&#xff08;Java non-blocking IO&#xff09;非阻塞IONIO的三大组件 Channel Selector BufferBuffer&#xff08;缓冲区&#xff09;Channel&#xff08;通道&#xff09;Channe的分类&#xff0c;与Buffer的…

【Spring(六)】使用篇:AOP在开发中的使用

有关Spring的所有文章都收录于我的专栏&#xff1a;&#x1f449;Spring&#x1f448; 目录 一、前言 二、演示 三、切面类中声明通知方法 四、使用 相关文章 【Spring&#xff08;一&#xff09;】如何获取对象&#xff08;Bean&#xff09;【Spring&#xff08;一&#xff09…

刷爆力扣之数组形式的整数加法

刷爆力扣之数组形式的整数加法 HELLO&#xff0c;各位看官大大好&#xff0c;我是阿呆 &#x1f648;&#x1f648;&#x1f648; 今天阿呆继续记录下力扣刷题过程&#xff0c;收录在专栏算法中 &#x1f61c;&#x1f61c;&#x1f61c; 该专栏按照不同类别标签进行刷题&…

Nodejs -- 一文了解Express模块

文章目录1. 初识Express1.1 Express简介1.1.1 什么是Express1.1.2 进一步理解Express1.1.3 Express能做什么1.2 Express的基本使用1.2.1 安装1.2.2 基本使用1.2.3 监听GET请求1.2.4 监听POST请求1.2.5 把内容响应给客户端1.2.6 获取URL中携带的查询参数1.2.7 获取URL中的动态参…

一文读懂TCP的三次握手(详细图解)

在学习TCP三次握手的过程前&#xff0c;首先熟悉几个缩写简称&#xff1a; TCB 传输控制块&#xff0c;打开后服务器/客户端进入监听&#xff08;LISTEN&#xff09;状态 SYNTCP报文标志位&#xff0c;该位为1时表示发起一个新连接ACKTCP报文标志位&#xff0c;该位为1时&…

傻白入门芯片设计,如何降低CPU功耗?(八)

低功耗芯片设计是本世纪以来最重要的新兴设计方法。可以说没有低功耗设计&#xff0c;就没有今天的智能手机&#xff0c;移动设备&#xff0c;物联网&#xff0c;及高性能计算等产业。随着芯片图形尺寸越来越小&#xff0c;低功耗设计在现在及未来的芯片中会起到越来越重要的作…