Java多线程基础复习

news2024/11/27 22:26:07

文章目录

  • 多线程
    • 1.进程
      • 进程属性
      • 并发和并行
      • 虚拟地址空间
    • 2.线程
      • 概念
      • 线程的创建方式
    • 3.Thread类
      • 常见构造方法和属性
      • 线程的状态
      • 优先级
      • 后台线程
      • 线程是否存活
      • start和run
    • 4. 线程的一些基本操作
      • 线程中断(interrupted)
      • 线程等待join
      • currentThread(获取当前线程引用)
      • 线程休眠sleep


多线程

1.进程

进程是操作系统中非常核心的一个概念,进程也叫做“任务”,一个运行起来的程序就称为进程,像QQ安装后是一个存储在磁盘的一个可执行程序(静态的),当双击QQ运行的时候操作系统就会把文件中的核心数据加载到内存里,同时在系统中生成一个进程,同时给进程分配一定的系统硬件资源(CPU、内存、磁盘、网络带宽等…),在任务管理器中就可以查看到。

在这里插入图片描述

进程属性

同一时刻系统中运行的进程是有很多的,这么多的进程是如何被操作系统管理的呢?操作系统管理进程是通过描述+组织管理进程。

  1. 描述:详细的描述清楚一个进程有哪些属性/信息,操作系统通过一个PCB来描述一个进程。
    • PCB:也叫进程的控制块,它是C语言的一个结构体,一个结构体对象就对应着一个进程
    • 一个进程可能是一个PCB,也可能对应多个
  2. 组织:通过一定的数据结构,把若干个用来描述的实体,给放到一起,并且进行增删改查。
    • 系统中通常会使用双向链表这样的结构来把这些PCB给组织在一起
    • 创建一个进程,本质就是创建PCB,并且加入到链表上
    • 销毁一个进程,本质上就是从链表上删除对应的PCB节点
    • 查看任务管理器的进程列表,本质上就是在遍历这个链表

那么PCB里具体有哪些信息?(进程里面有哪些关键的要素)

  1. pid:进程的身份标识,一个机器这些进程的pid是唯一的,通过pid来区分一个进程

  2. 内存指针

    • 一个可执行文件,双击后开始在内存中运行,操作系统把文件中的核心数据(要执行的指令、指令依赖的数据)加载到内存中
    • 既然要创建进程,就要给进程分配内存空间,然后在这个内存空间上就有很多区域
    • 内存指针就是指向进程持有的内存资源,在程序关闭时也方便释放内存资源。
  3. 文件描述符表

    • 每个进程都可以打开一些文件(文件其实就是存在硬盘上的数据)
    • 文件描述符表里面就记录了当前进程都打开了哪些文件(打开了之后就可以后续针对这些文件进行读写操作了)

    下面的这些属性都是和进程调度相关的

  4. 进程状态

    • 运行状态:进程正在CPU上运行
    • 就绪状态:进程已经做好准备,随时准备被CPU调度执行
    • 阻塞状态:进程在此状态下不能执行,只有等阻塞该进程的事假完成之后才能执行
  5. 进程的优先级

    • 系统调度的时候,会根据优先级来给进程安排运行时间
    • 进程优先级越高就越容易被CPU调度执行
    • 创建进程的时候,可以通过一些系统调用来干预优先级
  6. 进程的上下文

    • 进程在CPU上执行了一会之后,要切换给别的进程,就需要保存当前运行的中间结果(类似存档),下次进程再被调度执行的时候,恢复到之前的中间结果(类似读档),继续往下执行
    • 对于进程来说,上下文就是CPU中的寄存器的值(寄存器的值就包含了运行的中间结果,需要把这这写结果保存到PCB的上下文信息中(内存))
    • 进程的上下文主要是存储调度出CPU之前,寄存器中的信息(把寄存器信息保存到内存中),等到这个进程下次恢复到CPU上执行的时候,就把内存中保存好的数据恢复到寄存器中
  7. 进程的记账信息

    • 记账信息主要是记录进程在CPU上执行多久了,用来辅助决定这个进程是继续执行,还是要被调度出CPU了
    • 通过进程记账信息就可以让进程运行更加均衡,避免有进程完全到不了CPU上执行

并发和并行

电脑上有着几百个进程都在运行,但是电脑只有1个CPU,而且一般都是4核或者8核心的CPU,是不足以运行这么进程的。操作系统就采用了进程调度这样的机制来进行执行的。

并发执行

并发执行是指一个CPU运行多个进程,一个CPU先运行进程1、再运行进程2…,这样调度执行,虽然CPU在一直进行切换,但是在电脑前坐着的使用者是感受不到这个过程的。

并行执行

并行执行是指多个CPU运行着多个进程,比如CPU1运行进程1,CPU2运行进程2,进程1和进2无论是从微观还是宏观都是同时执行的,

虚拟地址空间

一个进程想要运行,就需要给它分配一些系统资源,其中内存就是最核心的资源。

虚拟地址空间是指一个进程可用的地址空间,它是在进程被创建时由操作系统给出的,它是一种特殊的地址空间,它使得每个进程都可以访问自己的一块独立的内存空间,而不需要关心实际的物理地址。

MMU是计算机硬件中用于管理虚拟内存和物理内存之间映射的芯片,MMU通过将虚拟地址从CPU发出的程序地址装换为物理地址,来管理内存和提供进程之间的保护。

在这里插入图片描述

也就是我们访问的内存是虚拟内存而不是真实的物理内存,MMU会对我们的内存访问进行校验,判断是否越界访问,只有合法访问才能正常访问内存,如果越界就会MMU就会给操作系统发送异常信息。

通过虚拟地址空间,操作系统可以管理活跃的进程和内存,同时也提供了更好的保护机制,从而确保系统的安全性和可靠性。

由于进程之间相互隔离,进程间的通讯又是一个新的问题。可以使用文件或者socket等两个进程都可以访问的公共资源。

2.线程

概念

线程就是一个“执行流”,可以理解为线程是一个“轻量级进程”。虽然进程已经可以实现“并发编程”,但是频繁创建和销毁进程,开销还是比较大的,引入多线程是对多进程程序的优化。

  • 创建线程比创建进程更加高效
  • 销毁线程比销毁进程更加高效
  • 调度线程比调度进程更加高效
  • 同一个进程中的这些线程之间,共用同一份系统资源(内存+文件描述符表)

创建线程并没有向操作系统申请资源,销毁线程也不需要释放资源,线程是产生在进程内部,共用之前的资源。进程包含了线程,一个线程对应一个PCB,一个进程对应一组PCB(内存指针和文件描述符表,都是一份,但状态、优先级、记账信息、上下文、每个线程都有独立的)。进程是操作系统分配资源的基本单位,线程是调度执行的基本单位。

创建线程其实就是在内核里创建了PCB

线程的创建方式

1.继承Thread类重写run方法

public class ThreadDemo {
    static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("继承Thread重写run方法");
        }
    }

    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
    }
}

2.实现Runnable接口,重写run方法

public class ThreadDemo {
    static class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("实现Runnable接口重写run方法");
        }
    }
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
    }
}

3.使用Thread匿名内部类

public static void main(String[] args) {
    Thread thread = new Thread() {
        @Override
        public void run() {
            System.out.println("使用匿名内部类");
        }
    };
    thread.start();
}

4.使用Runnable匿名内部类

public static void main(String[] args) {
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Runnable匿名内部类");
        }
    });
    thread1.start();

}

5.使用lambda表达式

public static void main(String[] args) {
    Thread thread2 = new Thread(()->{
        System.out.println("使用lambda表达式");
    });
    thread2.start();
}

6.使用Callable+FutureTak

  • Callable和Runnable类似都是描述了一个过程,只不过Callable带有有返回值。Callable的泛型参数就是返回值
  • Callable中包含call()方法,和Runnable的run()方法类似,不过call()方法是带有返回值的
  • 通过FutureTask的get()方法来获取Callable的返回值,如果此时还没有获取到返回值,该方法就会阻塞.
public static void main(String[] args) throws ExecutionException, InterruptedException {
        //这是一个能有返回值的线程,也是一个接口
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                //Thread.sleep(4000);
                int sum = 0;
                for (int i = 0; i <= 100000; i++) {
                    sum+=i;
                }
                return sum;
            }
        };
        //通过 FutureTask 来接收 Callable的返回值
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        //调用t.start() 就会执行 FutureTask() 内部的 call 方法,完成计算,计算结果就会返回到 FutureTask对象中
        t.start();
        System.out.println("hhh");
        //调用FutureTask的 get 方法就能获取到结果
        //如果FutureTask没有接受到值就会阻塞等待
        int tmp = futureTask.get();
        System.out.println(tmp);
}

3.Thread类

常见构造方法和属性

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable对象创建线程
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,分好的组即为线程组

Thread常用方法

属性获取的方法
线程IdgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
获取当前线程对象Thread.currentThread()

线程的状态

Java中线程的状态是和操作系统的状态不一样,这是Java自己的一套线程状态。Java中的线程状态其实主要是就绪状态和阻塞状态。

  • NEW:Thread对象创建出来来,但是内核的PCB还没有创建(还没有真正创建线程)
  • TERMINATED:内核的PCB销毁了,但是Thread对象还在
  • RUNNABlE:就绪状态(线程正在CPU上运行或者是在就绪队列中排队)
  • TIMED_WATING:安装一定的时间进行阻塞,sleep或者其它指定时间阻塞
  • WAITING:特殊的阻塞状态,调用wait时
  • BLOCKED:等待锁的时候进入的阻塞状态

优先级

优先级,也是和"进程的优先级”是类似的效果,此处的状态和优先级,和内核PCB中的状态优先级并不完全一致。

后台线程

关于后台线程(守护线程),我们创建的线程默认都是“前台线程”,前台线程会阻止进程退出,如果main运行完了,前台线程还没有执行完毕,进程是不会退出的。

如果是后台线程,后台线程是不阻止进程退出的,如果main等其他的前台线程执行完了,这个时候,即使后台线程没有执行完,进程也会退出。

线程是否存活

判断一个线程是否存活,最简单的方法就是看run方法是否已经结束。

public static void main(String[] args) {
    Thread thread = new Thread() {
        @Override
        public void run() {
            System.out.println("使用匿名内部类");
        }
    };
    thread.start();
    //下面还有一些逻辑
    //......
}

比如上述代码,run方法执行完毕后,其实线程就销毁了,但是由于Thread对象是靠JVM的GC来进行销毁的,所以它和内核的线程生命周期是不一样的,它会比内核的线程存活时间更长,所以此时就可以使用isAlive()方法来判断线程是否存活。

start和run

start()方法会在内核创建新的线程,也就是创建了新的PCB,此时代码就是多线程的方式执行,而如果直接调用的是run()方法,就并不会在内核创建新的线程,也就是说此时代码是串行执行,和多线程没有任何关系。

public static void main(String[] args) {
    Thread thread = new Thread() {
        @Override
        public void run() {
            System.out.println("使用匿名内部类");
        }
    };
}

4. 线程的一些基本操作

线程中断(interrupted)

如果一个线程的run方法执行完了,线程就已经结束了,但实际应用中可能是一没那么快结束,甚至可能是一个死循环,那么想让线程结束就需要用到线程中断了。

  1. 直接定义一个变量作为一个标记位判断线程是否结束(并不推荐)

    public class Interrupted {
        private static boolean FLAG = true;
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(()->{
                while (FLAG) {
                    System.out.println("test");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
            Thread.sleep(5000);
            FLAG = false;
        }
    }
    
  2. 使用标准库中的标记位

    方法说明
    public void interrupt()中断对象关联线程,如果线程正在阻塞,则以异常方式通知,否则设置标记位
    public static boolean interrupted()判断当前线程的中断标志位是否设置,调用后清除标志位(默认返回false)
    public boolean isInterrupted()判断对象关联的线程的标志位是否设置,调用后不清除标志位(默认返回false)

    代码示例:

    public static void demo2() throws InterruptedException {
        Thread thread = new Thread(()->{
            while (!Thread.interrupted()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("test");
            }
        });
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
    

    interrupt() 方法本来是会把isInterrupted()的标志位修改为ture的,但这段代码在阻塞就会抛出一个异常,且线程不会停止循环继续运行。

    这里的 interrupt 方法有两种行为

    1.如果当前线程正在运行中,此时就会修改 Thread.islnterruppted() 标记位为 true

    2.如果当前线程 正在 sleep、wait、等待锁,此时就会触发 InterruptedException

    如果要结束循环在catch加上brak即可

    public static void demo2() throws InterruptedException {
        Thread thread = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
                System.out.println("test");
            }
        });
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
    

    还有一个静态的方法interrupted()也是标志位

    public static void  demo3() throws InterruptedException {
            Thread thread = new Thread(()->{
                while (!Thread.interrupted()) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        break;
                    }
                    System.out.println("test");
                }
            });
            thread.start();
            Thread.sleep(5000);
            thread.interrupt();
        }
    

那么interrupted()isInterrupted()方法有什么区别呢?

列如:调用 interrupt() 方法,把标记位设为 true,就应该结束循环

  • 当调用 静态的 interrupted 来判定标记位的时候,就会返回 true,同时就会把标记位再改回 false,下次再调用interrupted() 就返回 false
  • 如果是调用非静态的 isInterrupted() 来判断标记位,也会返回 true,但不会对标记位进行修改,后面再调用isInterrupted() 的时候仍然返回 true

线程等待join

线程之间的调度顺序,是不确定的。可以通过一些特殊的操作,来对线程的执行顺序,做出干预。其中

join就是一个办法,控制线程之间的结束顺序。

比如这里在main方法里调用join的效果就是等thread线程的代码执行完毕后才继续执行main方法里的逻辑,此时main方法的线程就进入阻塞状态,不参与cpu调度。

当然根据需要join可以设置指定时间的等待,正常使用一般不会死等的。

public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("线程执行中...");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        thread.join();
        System.out.println("线程执行结束");
    }

currentThread(获取当前线程引用)

currentThread 能够获取到当前线程对应的 Thread 实例的引用,相当于 this关键字

public static void demo() {
    Thread thread = new Thread(){
        @Override
        public void run() {
            System.out.println(this.getId());
            System.out.println(Thread.currentThread().getId());
        }
    };
}

但是需要注意的是,如果是使用 Runnable 或者 lambda 的方式来创建的线程,就无法使用 this 了。
this指向的是 Runnable 实例,而不是Thread 实例了,此时也就没有 getId 方法了。

public static void demo1() {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getId());
            //System.out.println(this.getId); 错误写法
        }
    });
}

线程休眠sleep

也是我们比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的
通过 sleep() 方法来休眠一个线程,sleep() 是一个类方法

public static void main(String[] args) {
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //休眠1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
}

Sleep 这个方法,本质上就是把线程PCB给从就绪队列,移动到了阻塞队列,只有当 Sleep时间到了或者抛出异常了才会回到就绪队列中

在这里插入图片描述


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

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

相关文章

安全开发-JS应用原生开发JQuery库Ajax技术加密编码库断点调试逆向分析元素属性操作

文章目录 JS原生开发-文件上传-变量&对象&函数&事件JS导入库开发-登录验证-JQuery库&Ajax技术JS导入库开发-编码加密-逆向调试 JS原生开发-文件上传-变量&对象&函数&事件 1、布置前端页面 2、JS获取提交数据 3、JS对上传格式判断 <script>…

Spring MVC 详解(2)

目录 3. 返回数据的功能 3.1 返回静态页面 3.2 请求转发 VS 请求重定向 前言 上一篇文章已经介绍了 Spring MVC 框架的两个功能&#xff0c;和前端建立连接的功能和从前端获取参数的功能&#xff0c;还有最后一个功能就是 输出数据的功能。 3. 返回数据的功能 返回的数据大致…

2min搞定 mac pycharm新建导入python项目

mac pycharm新建和导入python项目&虚拟环境配置&下载类库 一、通用设置step1 、通过自定义配置&#xff0c;指定默认虚拟环境变量step2、设置虚拟环境和指定默认工作空间step3 、导入或者新建python项目 二、pycharm新建python项目step1、点击新建【file->newProjec…

appscan 应用

HCL appscan是个常见的web app DAST 扫描工具 有企业版和standalone 版本。大家常用的都是单机版本。企业版平台&#xff0c;集成了IAST。 appscan 使用比较简单&#xff0c;基本输入url 账号密码就开扫了。 用了一段时间几点体验 1 还是需要手动explore的&#xff0c;他自…

ShardingSphere分库分表实战之读写分离原理解析

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

MFC第十九天 记事本项目功能完善和开发、CTabCtrl类与分页模式开发

文章目录 记事本项目功能完善和开发查找界面的记忆功能 、使用F3快捷键自动向下查找功能 的开发单次替换的算法研究 CFileDialog 构造函数详解 应用另存为时选择编码 &#xff08;三种方案&#xff09;vista 样式文件对话框 bVistaStyle 为TRUE时 1pch.hCApp NotePad.cpp 对编码…

《Docker与持续集成/持续部署:构建高效交付流程,打造敏捷软件交付链》

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【算法基础:搜索与图论】3.5 求最小生成树算法(PrimKruskal)

文章目录 最小生成树介绍朴素Prim算法算法思路⭐例题&#xff1a;858. Prim算法求最小生成树 Kruskal算法算法思路⭐例题&#xff1a;859. Kruskal算法求最小生成树 最小生成树介绍 最小生成树 有关树的定义 生成子图&#xff1a;生成子图是从原图中选取部分节点以及这些节点…

16.喝水

喝水 html部分 <h1>Goal: 2 Liters</h1> <div class"cup cupbig"><div class"remained"><span id"liters">2L</span><small>Remained</small></div><div class"percentage&quo…

欧姆龙cp1h-e串口以太网连接怎么设置欧姆龙CX系列

捷米特JM-ETH-CX串口转以太网通讯处理器是为满足日益增多的工厂设备信息化需求&#xff08;设备网络监控和生产管理&#xff09;而设计&#xff0c;用于欧姆龙 CPM、CQM、C200、C1000、C2000 等多个系列 PLC 的以太网数据采集&#xff0c; 非常方便构建生产管理系统。 捷米特J…

【字符流】案例:文件到集合(改进版)

案例&#xff1a;文件到集合&#xff08;改进版&#xff09; 1.需求&#xff1a; 把文本文件中的数据读取到集合中&#xff0c;并遍历集合。要求&#xff1a;文件中每一行数据是一个学生对象的成员变量值 ​ 举例&#xff1a;001,郝佳乐,20,西安 2.思路&#xff1a; 定义学…

Vue第六篇:电商网站图片放大镜功能

本文参考&#xff1a;https://blog.csdn.net/liushi21/article/details/127497487 效果如下&#xff1a; 功能实现分解如下&#xff1a; &#xff08;1&#xff09;商品图区域&#xff1a;主要是浏览图片&#xff0c;根据图片的url显示图片。当鼠标离开此区域时"放大镜区…

HTTP中GET请求和POST请求的区别

前言 HTTP&#xff08;超文本传输协议&#xff09;是用于在 Web 浏览器和 Web 服务器之间传输数据的协议。在 HTTP 中&#xff0c;GET 和 POST 是两种常见的请求方法。一般我们在浏览器输入一个网址访问网站都是 GET 请求&#xff1b;在 FORM 表单中&#xff0c;可以通过设置 …

Mac端简单好用的程序创建工具:VMware InstallBuilder Enterprise

VMware InstallBuilder Enterprise for Mac是一款用于为台式机和服务器软件构建跨平台安装程序的开发工具。使用InstallBuilder&#xff0c;您可以从单个项目文件和构建环境中为Linux&#xff0c;Windows&#xff0c;Mac OS X&#xff0c;Solaris和其他平台快速创建动态&#x…

Windows下mosquitto服务端和MQTT.fx客户端搭建模拟环境

第一部分 Mosquitto安装测试 一、概念梳理 1、Mosquitto是一款实现了消息推送协议MQTT 3.1的开源消息代理软件&#xff0c;提供轻量级的、支持可订阅/可发布的消息推送模式&#xff0c;是设备与设备之间的短消息通信变得简单&#xff0c;广泛应用于低功耗传感器、手机&#xff…

django跨域设置

1.安装 (venv) ***\data_analyse_web>pip install django-cors-headers 2.添加应用 :在settings.py中添加应用,放到任意位置都行 INSTALLED_APPS {# ...corsheaders,# ... } 3. 设置中间层&#xff0c;在settings.py中添加中间层&#xff0c;放到最前面 MIDDLEWARE [c…

【设计模式】23种设计模式——原型模式Prototype(原理讲解+应用场景介绍+案例介绍+Java代码实现)

原型模式 介绍 原型模式指用通过拷贝原型实例创建新的实例&#xff0c;新实例和原型实例的属性完全一致原型模式是一种创建型设计模式工作原理是通过调用原型实例的 clone()方法来完成克隆&#xff0c;原型实例需要实现Cloneable接口&#xff0c;并重写clone()方法需要为每个…

WSR-88D天气雷达工作模式、监测目标、反射率含义讲解

一、WSR-88D 简介 WSR-88D是天气监视多普勒雷达之一。自 1988 年首次建造和测试以来,它已在包括阿拉斯加和夏威夷在内的美国 160 多个地点安装和使用。WSR-88D也已安装在波多黎各和太平洋的几个岛屿。 WSR-88D雷达发射功率为750000瓦(平均灯泡只有75瓦)!这种功率使雷达产…

03. 自定义镜像 Dockerfile

目录 1、前言 2、构建镜像的方式 2.1、docker commit 2.1.1、先查看下当前的容器 2.1.2、生成该容器镜像 2.1.3、查看镜像列表 2.2、Dockerfile 2.2.1、创建Dockerfile文件 2.2.2、编写Dockerfile文件 2.2.3、构建镜像 2.2.4、使用该镜像生成容器 3、Dockerfile 3…

Docker 网络、资源控制

Docker 网络、资源控制 一、Docker 网络1、Docker 网络实现原理2、Docker 的网络模式&#xff1a;1&#xff0e;host模式2&#xff0e;container模式3&#xff0e;none模式4&#xff0e;bridge模式5&#xff0e;自定义网络 二、资源控制1&#xff0e;CPU 资源控制&#xff08;1…