多线程的初识和创建

news2024/7/6 19:35:44

请添加图片描述

✨个人主页:bit me👇
✨当前专栏:Java EE初阶👇
✨每日一语:知不足而奋进,望远山而前行。

目 录

  • 💤一. 认识线程(Thread)
    • 🍎1. 线程的引入
    • 🍏2. 线程是什么?
    • 🍋3. 进程和线程之间的区别
  • 💨二. 第一个多线程程序
    • 🏐1. Java中线程的认识和基本操作
    • 🥎2. 线程的查看
  • 🎶三. 创建线程的几种常见写法
  • 💦四. 多线程的优势
  • ❄️五. 多线程的使用场景

💤一. 认识线程(Thread)

🍎1. 线程的引入

  • 根据我们前面学的进程,为什么要有多个进程呢?为了并发编程,他的 CPU 单个核心已经发挥到极致了,想要提升算力,就得使用多个核心。
  • 引入并发编程,最大的目的就是为了能够充分的利用好 CPU 的多核资源。使用多进程这种编程模型,是完全可以做到。并发编程,并且也能够使 CPU 多核被充分利用,但是在有些情急下,会存在问题,例如:需要频繁的创建 / 销毁进程,这个时候就会比较低效!!!
  • 例如,你写了个服务器程序,服务器要同一时刻给很多客户提供服务的,就需要用到并发编程,典型的做法,就是每个客户端给他分配一个进程,提供一对一服务。

创建 / 销毁进程,本身就是一个比较低效的操作
1. 创建 PCB
2. 分配系统资源(尤其是内存资源)分配资源,就比较消耗时间了!(这个是在系统内核资源管理模块进行一系列操作的...)
3. 把 PCB 加入到内核的双向链表中

为了提高这个场景下的效率,就引入了 " 线程 ",线程其实也叫做 " 轻量级进程 "

🍏2. 线程是什么?

一个线程其实是包含在进程中的(一个进程里面可以有很多个线程),每个线程其实也有自己的 PCB (一个进程里面可能就对应多个 PCB),同一个进程里面的多个线程之间,共用一份系统资源(意味着,新创建的线程,不必重新给他分配系统资源,只需要复用之前的即可!)

因此,创建线程只需要
1. 创建 PCB
2. 把 PCB 加入到内核的链表中

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

  • 创建线程比创建进程更快,开销更小.
  • 销毁线程比销毁进程更快,开销更小.
  • 调度线程比调度进程更快.

线程,是包含在进程内部的 " 逻辑执行流 "(线程可以执行一段单独的代码,多个线程之间,是并发执行的)

操作系统进行调度的时候,其实也是以 " 线程为单位 " 来进行调度的(系统内核不认进程 / 线程,只认 PCB)

如果把进程比作工厂,线程就是工厂内部的流水线

🍋3. 进程和线程之间的区别

  • 进程是包含线程的,线程是在进程内部的
  • 每个进程有独立的虚拟地址空间(进程之间的资源是独立的,进程的独立性),也有自己独立的文件描述符表;同一个进程(不同进程里面的不同线程,则没有共享的资源)的多个线程之间,则共用这一份虚拟地址空间和文件描述符表(线程之间系统资源是共享的)
  • 进程是操作系统中资源分配的基本单位,线程是操作系统中,调度执行的基本单位
  • 多个进程同时执行的时候,如果一个进程挂了,一般不会影响到别的进程;同一个进程内的多个线程之间,如果一个线程挂了,很可能把整个进程带走,其他同进程里的线程也就没了

 

💨二. 第一个多线程程序

🏐1. Java中线程的认识和基本操作

在 Java 中即使是一个最简单的 “hello”,其实在运行的时候也涉及到线程了,一个进程里至少会有一个线程

public static void main(String[] args) {
    System.out.println("hello");
}

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

注:

谈到多进程的时候,经常会谈到 “父进程” “子进程”。进程 A 里面创建了进程 B,A 是 B 的父进程,B 是 A 的子进程。但是在多线程里面,没有 “父线程” “子线程” 这种说法,线程之间的地位是对等的!

创建一个线程,Java 中创建线程,离不开一个关键的类,Thread。一种比较朴素的创建线程的方式,是写一个子类,继承 Thread,重写其中的 run 方法。

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

这个 run 方法重写的目的,是为了明确咱们新创建出来的线程,要干啥活儿

光创建了这个类,还不算创建线程,还得创建实例,在主函数中写如下代码

Thread t = new MyTread();
t.start();
  • System.out.println(“hello Thread!”); --> 先把新员工要做的任务安排好
  • Thread t = new MyTread(); --> 招聘进来一个新员工,把任务交给他
  • t.start(); --> 告诉新员工,你开始干活!

t.start(); --> 这才是真正开始创建线程(在操作系统内核中,创建出对应线程的 PCB,然后让这个 PCB 加入到系统链表中,参与调度)

Thread t = new MyTread();
t.start();

System.out.println("hello main!");

在这里插入图片描述
在这个代码中,虽然先启动的线程,后打印的 hello main,但是实际执行的时候,看到的却是先打印了 hello main 后打印了 hello thread。

  1. 每个线程是独立的执行流!main 对应的线程是一个执行流,MyThread 是另一个执行流,这两个执行流之间是并发(并发+并行)的执行关系!
  2. 此时两个线程执行的先后顺序,取决于操作系统调度具体实现(程序猿可以把这里的调度规则简单的视为是 “随机调度”)

因此执行的时候看到,是先打印 hello main 还是先打印 hello thread 是不能确定的。无论反复运行多少次看起来好像都是先打印 hello main,但是顺序仍然是不能确定的,当前看到的先打印 main,大概率是受到创建线程自身的开销影响的。

编写多线程代码的时候,一定要注意到!!!默认情况下,多个线程的执行顺序,是 “无序”,是 "随机调度"的。

我们还有一些手段能影响到线程执行的先后顺序的,但是调度器自身的行为修改不了,调度器仍然是随机调度,咱们最多是让某个线程先等待,等待另一个线程执行完了自己再执行

在这里插入图片描述

  • Process 进程
  • exit code 0 进程的退出码

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

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

要是我们不想要这个程序结束的这么快咋办?
就可以想办法让这个进程别结束这么快,好让咱们看下这里都有啥线程

while (true) {
    System.out.println("hello Thread!");
}
while (true){
    System.out.println("hello main!");
}

把他们都套上一个死循环之后看到打印结果是 hello main! 和 hello Thread! 交替打印

🥎2. 线程的查看

(注意不要关闭上述程序运行,查看线程需要)此时就可以查看当前 Java 进程里的线程有哪些(打开任务管理器可以观看到程序运行状态)

在这里插入图片描述

  1. 找出 JDK 安装的目录,找到 bin 文件里,在 JDK 里提供了 jconsole这样的工具,可以看到 Java 进程里的线程详情

在这里插入图片描述

  1. 点击运行(如果运行界面没有这样的选项,我们在打开的时候需要以管理员方式打开!)

在这里插入图片描述

  1. 点击进去即可(不用害怕hhh)

在这里插入图片描述

  1. 打开线程所在窗口

在这里插入图片描述

  1. 找到左下角,会有显示具体的线程

在这里插入图片描述

这个列表就是当前 Java 进程中的线程信息
其他的线程则是 JVM 中的辅助功能,有的线程负责垃圾回收,有的线程负责处理调试信息,比如咱们使用的 jconsole 能够连上这个 JVM ,也是靠对应的线程提供的服务

  1. 点击某个线程的时候,右侧会显示线程的详细情况

在这里插入图片描述

在这里插入图片描述

上面两张表里都描述了当前线程这里的各种方法之间调用的关联关系

这里的调用栈非常有用,未来调试一个 “卡死” 的程序的时候,就可以看下面每个线程的调用栈是啥,就可以初步的确认卡死的原因。

上面的死循环代码打印的太快太多,有时候我们不希望它这么快,不利于我们观察,于是用 sleep 来让线程适当休息一下

使用 Thread.sleep 的方式进行休眠,sleep 是Thread 的静态成员方法,sleep 的参数是一个时间,单位 ms

在这里插入图片描述

这个异常在多线程中经常能见到,interrupted 中断

通过 try-catch 抓捕异常后的运行结果

在这里插入图片描述

加上 sleep 之后,无序调度这个事情就被更加清楚的观察到了。

在这里插入图片描述

一个经典面试题:
谈谈 Thread 的 run 和 start 的区别:

  • 使用 start 可以看到两个线程并发的执行,两组打印交替出现
  • 使用 run 可以看到只是在打印 thread,没有打印 main
     

直接调用 run 并没有创建新的线程,而只是在之前的线程中,执行了 run 里的内容;使用 start,则是创建新的线程,新的线程里面会调用 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 Demo1 {
    public static void main(String[] args) {
        //创建一个线程
        //Java 中创建线程,离不开一个关键的类,Thread
        //一种比较朴素的创建线程的方式,是写一个子类,继承 Thread,重写其中的 run 方法。

        Thread t = new MyTread();
        //t.start();
        t.run();

        while (true){
            System.out.println("hello main!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

🎶三. 创建线程的几种常见写法

  1. 创建一个类继承 Thread,重写 run。(上面已经写过)

这个写法,线程和任务内容是绑定在一起的!

  1. 创建一个类,实现 Runnable 接口,重写 run。
Runnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
t.start();
  • 此处创建的 Runnable,相当于是定义了一个 "任务"(代码要干啥)
  • 需要 Thread 实例,把任务交给 Thread
  • 还是 Thread.start 来创建具体的线程
  • 这个写法,线程和任务是分离开的(更好的解耦合)(耦合的意思和表面差不多,模块 / 代码之间,关联关系越紧密,就认为耦合性越高,关联关系越不紧密,认为耦合性越低)
  • 写代码的时候追求 “高耦合” “低内聚”,让每个模块之间耦合性降低, 好处就是一个模块出了问题对另外的模块影响不大
  • 这种写法把任务内容和线程本身给分离开了,就把耦合性降低了(任务的本身和线程关系不大),假设这个任务不想通过多线程执行了,而是换成别的方式执行,这个时候代码的改动也不大。

一个问题:

为啥刚才使用 Thread,Runnable,interruptedException 都不需要 import ??啥样的类,不需要 import 就能直接使用??

  • 要么是同一个类中,要么这个类是在 java.lang 中(String)
  1. 仍然是使用继承 Thread 类,但是不再显式继承,而是使用 “匿名内部类”。
public class Demo3 {
    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 t = new Thread() {

此处就是在创建了一个匿名内部类(没有名字),这个匿名内部类是 Thread 的子类,同时前面 new 关键字,就给这个匿名内部类创建出了一个实例

这一套操作实现了继承,方法重写,实例化

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

Thread 实例是 Java 中对于线程的表示,实际上想要真正的跑起来,还是需要操作系统里面的线程,创建好了 Thread,此时系统里面还没有线程,直到调用 start 方法,操作系统才真的创建了线程(1.创建 PCB;2.把 PCB 加入到链表里)并进行运行。

  1. 使用 Runnable,是匿名内部类的方式使用
  • 方法一:
public class Demo4 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread t = new Thread(runnable);
        t.start();
    }
}
  • 方法二:(推荐)
public class Demo4 {
    public static void main(String[] args) {
	Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println("hello Thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();
    }
}

中间的匿名内部类的实例,是 Thread 的构造方法的参数!

  1. 使用 lambda 表达式,来定义任务。(推荐做法)
public class Demo5 {
    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 表达式这样的,能够简化代码编写的语法规则,称为 “语法糖”。

lambda 表达式本质上就仅仅是一个 “匿名函数”(没有名字的,只使用一次的函数)

实际上,线程还有其他的创建方式,后续介绍后面的创建方式!

  • 6.基于 Callable / FutureTask 的方式创建
  • 7.基于线程池的方式创建

至少有七种创建方式~

 

💦四. 多线程的优势

  1. 单个线程,串行的,完成 20 亿次自增。
public class Demo6 {
    private static final long COUNT = 20_0000_0000;

    private static void serial(){
        //需要把方法执行的时间给记录下来
        //记录当前的毫秒级时间戳
        long beg = 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 - beg) + " ms");
    }

    public static void main(String[] args) {
        serial();
    }
}

运行结果:

在这里插入图片描述

使用一个线程串执行花了大约 1200 ms

  1. 两个线程,并发的,完成 20 亿次自增
public class Demo6 {
    private static void concurrency(){
        long beg = 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 - beg) + " ms");
    }


    public static void main(String[] args) {
        concurrency();
    }
}

这个代码涉及到三个线程 t1,t2,main(调用 concurrency 方法的线程),三个线程都是并发执行的

t1.start(); t2.start(); 表示 t1,t2 是会开始执行的,同时,不等 t1,t2 执行完毕,main 线程就往下走了,于是就结束计时。此处的计时,是为了衡量 t1,t2 的执行时间,正确的做法应该是等到 t1,t2 都执行完,才停止计时。

在上述代码 t1.start(); t2.start(); 之后加上如下代码即可

try {
    t1.join();
    t2.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

t1.join(); t2.join(); --> join 是等待线程结束(等待线程把自己的 run 方法执行完),在主线程中调用 t1.join,意思就是让 main 线程等待 t1 执行完,这两个 join 操作谁先谁后不影响。

运行结果:

在这里插入图片描述

如果没有 join 的限制,main 和 t1,t2 都是同时往下走的!多个线程并发执行的(同时在往下走),走的过程中,调度顺序不确定!调度在这个代码执行过程中不是只有一次,会有很多次,即使是上面的简单代码,也可能会出现成千上万次的调度!绝对不是说一个线程执行完了再调度给第二个线程!!!

上面的结果相较之下对比体现出两个线程并发执行确实快了不少,但是为什么两个线程并发执行不是单个线程串行执行的耗时一半呢?

  • 创建线程自身,也是有开销的
  • 两个线程在 CPU 上不一定是纯并行,也可能是并发(一部分时间并行了,一部分时间并发了)
  • 线程的调度,也是有开销的

 

❄️五. 多线程的使用场景

  1. 在 CPU 密集型

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

  1. 在 IO 密集型场景

input output ,读写硬盘,读写网卡…这些操作都算 IO,像这些 IO 操作,几乎都是不消耗 CPU 就能完成快速读写数据的操作,既然 CPU 在摸鱼,就可以给他找点活儿干,也可以使用多线程,避免 CPU 过于闲置。

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

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

相关文章

【计算机网络:自顶向下方法】Chapter4 网络层:数据平面

Chapter44.1 网络层概述4.1.1 网络层服务4.1.2 网络层的主要功能转发&#xff08;局部&#xff09;路由选择&#xff08;全局&#xff09;4.1.3 控制平面和数据平面传统方式SDN方式4.1.4 网络服务模型4.2 路由器组成4.2.1 路由器结构概况4.2.2 转发方式4.2.3 输入端口处理与基于…

什么是api应用程序接口?

API:应用程序接口(API:Application Program Interface) 应用程序接口是一组定义、程序及协议的集合&#xff0c;通过 API 接口实现计算机软件之间的相互通信。API 的一个主要功能是提供通用功能集。程序员通过调用 API 函数对应用程序进行开发&#xff0c;可以减轻编程任务。…

【华为OD机试模拟题】用 C++ 实现 - TLV 编码(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明TLV 编码题目输入输出描述示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看…

大数据技术之Hadoop

第1章 Hadoop概述1.1 Hadoop是什么1.2 Hadoop发展历史&#xff08;了解&#xff09;1.3 Hadoop三大发行版本&#xff08;了解&#xff09;Hadoop三大发行版本&#xff1a;Apache、Cloudera、Hortonworks。Apache版本最原始&#xff08;最基础&#xff09;的版本&#xff0c;对于…

模型类的编写有没有什么靠谱的优化方法?

模型类的编写需要私有属性&#xff0c;setter...getter...方法、toString方法 和构造函数。虽然这些内容不难&#xff0c;同时也都是通过IDEA工具生成的&#xff0c;但是过程还是必须得走一遍&#xff0c;那么对于模型类的编写有没有什么优化方法?可以通过Lombok来实现优化。L…

C语言--指针进阶2

目录前言函数指针函数指针数组指向函数指针数组的指针回调函数前言 本篇文章我们将继续学习指针进阶的有关内容 函数指针 我们依然用类比的方法1来理解函数指针这一全新的概念&#xff0c;如图1 我们用一段代码来验证一下&#xff1a; int Add(int x, int y) {return xy;…

idea报错idea start filed

今天遇到idea启动失败的问题 问题分析&#xff1a; address already in use&#xff1a;bind idea需要的端口被占用 解决 重启就行&#xff0c;重启会重新分配端口。 官方解决 查看给的网站地址&#xff0c;这里官方给出的原因&#xff08;访问好慢&#xff0c;搭梯子我才…

图节点嵌入相关算法学习笔记

引言 本篇笔记为coggle 2月打卡任务&#xff0c;正好也在学习cs224w&#xff0c;干脆就一起做了&#xff0c;以下是任务列表&#xff1a; 任务名称难度任务1&#xff1a;图属性与图构造低、1任务2&#xff1a;图查询与遍历低、2任务3&#xff1a;节点中心性与应用中、2任务4&…

Spark计算框架入门笔记

Spark是一个用于大规模数据处理的统一计算引擎 注意&#xff1a;Spark不仅仅可以做类似于MapReduce的离线数据计算&#xff0c;还可以做实时数据计算&#xff0c;并且它还可以实现类似于Hive的SQL计算&#xff0c;等等&#xff0c;所以说它是一个统一的计算引擎 既然说到了Spar…

js 拖动--动态改变div的宽高大小

index.html 如下&#xff1a;&#xff08;可以新建一个index.html文件直接复制&#xff0c;打开运行&#xff09; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible&qu…

Python tkinter -- 第18章 画布控件之窗口

18.2.22 create_window(position, **options) 可以在画布控件中放置其他tkinter控件。放置的方法就是使用窗口组件。一个窗口组件只能容纳一个控件。如果要放置多个控件&#xff0c;可以把这些控件作为Frame控件的子控件&#xff0c;将Frame控件放入窗口组件中&#xff0c;就可…

超简单 华为OD机试用Python实现 -【踢石头子,踢石子问题】(2023-Q1 新题)

华为OD机试题 华为OD机试300题大纲踢石头子,踢石子问题题目输入输出示例一输入输出Python 代码如下所示算法思路华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.net/hihell/categ…

ChatGPT似乎有的时候并不能搞懂Java的动态分派,你懂了吗?

目录 碎碎念 ChatGPT 中出现的问题 那么正确答案应该是什么呢&#xff1f; 分派的相关知识点总结&#xff1a; 分派是什么&#xff1f; 静态分派与动态分派&#xff1a; Java语言是静态多分派&#xff0c;动态单分派的&#xff1b; 静态分派&#xff1a;静态重载多分派…

追梦之旅【数据结构篇】——详解C语言实现二叉树

详解C语言实现二叉树~&#x1f60e;前言&#x1f64c;什么是二叉树&#xff1f;二叉树的性质总结&#xff1a;整体实现内容分析&#x1f49e;1.头文件的编写&#xff1a;&#x1f64c;2.功能文件的编写&#xff1a;&#x1f64c;1&#xff09;前序遍历的数值来创建树——递归函…

IGKBoard(imx6ull)-Input设备编程之按键控制

文章目录1- input子系统介绍2- input事件目录&#xff08;1&#xff09;struct input_event 结构体&#xff08;2&#xff09;type&#xff08;事件类型&#xff09;&#xff1a;&#xff08;3&#xff09;code&#xff08;事件编码&#xff09;&#xff08;4&#xff09;value…

【华为OD机试模拟题】用 C++ 实现 - 九宫格按键输入(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明九宫格按键输入题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高…

webp格式转换成png怎么转

相对于png 图片&#xff0c;webp比png小了45%&#xff0c;但是缺点是你压缩的时候需要的时间更久了&#xff1b;优点是体积小巧&#xff1b;缺点是兼容性不太好, 只有opera,和chrome支持&#xff0c;不仅如此在后期的编辑修改上也很多软件无法打开。所以我们通常要将webp格式转…

9.1 IGMPv1实验

9.4.1 IGMPv1 实验目的 熟悉IGMPv1的应用场景掌握IGMPv1的配置方法实验拓扑 实验拓扑如图9-7所示&#xff1a; 图9-7&#xff1a;IGMPv1 实验步骤 &#xff08;1&#xff09;配置IP地址 MCS1的配置 MCS1的IP地址配置如图9-8所示&#xff1a; 图9-8&#xff1a;MCS1的配置 …

xgboost学习-XGBoost的智慧

文章目录一、选择弱评估器&#xff1a;重要参数booster二、XGB的目标函数&#xff1a;重要参数objective三、求解XGB的目标函数四、参数化决策树 alpha&#xff0c;lambda五、寻找最佳树结构&#xff1a;求解 ω与T六、寻找最佳分枝&#xff1a;结构分数之差七、让树停止生长&a…

redis(10)事务和锁机制

Redis事务定义 Redis 事务是一个单独的隔离操作&#xff1a;事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中&#xff0c;不会被其他客户端发送来的命令请求所打断。 Redis 事务的主要作用就是串联多个命令防止别的命令插队。 Multi、Exec、discard Redis 事务中…