实现线程的多种方式锁的介绍ThreadLocal线程池 详细总结(下)

本文主要介绍线程池的基本使用

上述其他介绍在上一篇文章中:实现线程的多种方式&锁的介绍&ThreadLocal&线程池 详细总结(上)-CSDN博客

线程池

5.1、为什么使用线程池

线程池可以看做是管理了 N 个线程的池子,和连接池类似

5.2、认识线程池

5.2.1、线程池继承体系
Java 1.5 之后就提供了线程池 ThreadPoolExecutor ,它的继承体系如下:
        ThreadPoolExecutor :线程池
        Executor: 线程池顶层接口,提供了 execute 执行线程任务的方法
        Execuors: 线程池的工具类,通常使用它来创建线程池

 示例:

        public static void main(String[] args) {
            ExecutorService executorService = Executors.newFixedThreadPool(5);
            for (int i = 0 ; i < 200 ; i++){
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        //有5个线程在执行
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();

                        }
                        System.out.println(Thread.currentThread().getName()+":线程执 行了...");
                    }
                });
            }
        }
    }
运行结果:

5.3、线程池原理 

5.3.1、执行流程
我们以一个生活中的举例来理解:
1. 老陈要开软件公司,合伙几个核心的程序员做开发 : ( 线程核心数 )
2. 新的项目过来一个人接收一个项目去做,没有人手了,把新进来的项目放入项目排队池 ( 任务队列 )
3. 如果项目队列中的任务过多,需要招聘一些临时的程序员 ( 非核心线程 ) ,但是规定所有的开发总人数不能50( 最大线程数 )
4. 如果新的项目进来,核心程序员和临时程序员都没有人手了,并且项目队列也放满了,新来的项目该如何处理呢?
1 、拒绝 2 、丢弃老的项目做新的项目 3 、老陈自己做新的项目
线程提交优先级:核心 -> 队列 -> 非核心
线程执行优先级:核心 -> 非核心 -> 队列
1 、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
2 、当调用 execute() 方法添加一个任务时,线程池会做如下判断:
        a) 如果正在运行的线程数量小于 corePoolSize ,那么马上创建线程运行这个任务;
        b) 如果正在运行的线程数量大于或等于 corePoolSize ,那么将这个任务放入队列;
        c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize ,那么还是要创建非核 心线程立刻运行这个任务;
        d) 如果队列满了,而且正在运行的线程数量等于 maximumPoolSize ,那么线程池会抛出异常
RejectExecutionException
3 、当一个线程完成任务时,它会从队列中取下一个任务来执行。
4 、当一个线程无事可做,超过一定的时间( keepAliveTime )时,线程池会判断,如果当前运行的线程数大于 corePoolSize ,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
5.3.2、线程池核心构造器
线程池源码 ThreadPoolExecutor 构造器:

 线程池7个参数的构造器非常重要:

1 CorePoolSize: 核心线程数,不会被销毁
2 MaximumPoolSize : 最大线程数 ( 核心 + 非核心 ) ,非核心线程数用完之后达到空闲时间会被销毁
3 KeepAliveTime: 非核心线程的最大空闲时间,到了这个空闲时间没被使用,非核心线程销毁
4 Unit: 空闲时间单位
5 WorkQueue: 是一个 BlockingQueue阻塞队列,超过核心线程数的任务会进入队列排队         
        SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执 行新来的任务;
        LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为 Integer.MAX_VALUE
        ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小
6 ThreadFactory :线程工厂,用于创建线程池中线程的工厂方法,通过它可以设置线程的命名规则、优先级和线程类型。使用 ThreadFactory 创建新线程。 推荐使用 Executors.defaultThreadFactory
7 Handler: 拒绝策略,任务超过 最大线程数 + 队列排队数 ,多出来的任务该如何处理取决于 Handler
        AbortPolicy丢弃任务并抛出 RejectedExecutionException 异常;
        DiscardPolicy丢弃任务,但是不抛出异常;
        DiscardOldestPolicy丢弃队列最前面的任务,然后重新尝试执行任务;
        CallerRunsPolicy由调用线程处理该任务
可以定义和使用其他种类的 RejectedExecutionHandler 类来定义拒绝策略。

5.4、常见四种线程池 

Jdk 官方提供了常见四个静态方法来创建常用的四种线程 . 可以通过 Excutors 创建
1. CachedThreadPool :可缓存
2. FixedThreadPool :固定长度
3. SingleThreadPool :单个
4. ScheduledThreadPool :可调度
5.4.1CachedThreadPool
可缓存线程池,可以无限制创建线程
根据源码可以看出:
        这种线程池内部没有核心线程,线程的数量是有限制的最大是Integer 最大值
        在创建任务时,若有空闲的线程时则复用空闲的线程( 缓存线程 ) ,若没有则新建线程
        没有工作的线程(闲置状态)在超过了60S 还不做事,就会销毁
        适用:执行很多短期异步的小程序或者负载较轻的服务器
实战:
运行结果:
5.4.2FixedThreadPool  

根据源码可以看出:
        该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲置状态超 时而被销毁
        如果当前线程数小于核心线程数,并且也有闲置线程的时候提交了任务,这时也不会去复用之前的 闲置线程,会创建新的线程去执行任务(必须达到最大核心数才会复用线程)。如果当前执行任务 数大于了核心线程数,大于的部分就会进入队列等待。等着有闲置的线程来执行这个任务
        适用:执行长期的任务,性能好很多  
实战:
    public class fixedThreadPool {
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newFixedThreadPool(5);
            for (int i = 0; i < 150; i++) {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        //有5个线程在执行
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ":线程执 行...");
                    }
                });
            }
        }
    }
运行效果:
5.4.3SingleThreadPool

根据源码可以看出:

        有且仅有一个工作线程执行任务
        所有任务按照指定顺序执行,即遵循队列的入队出队规则。
        适用:一个任务一个任务执行的场景。 如同队列
实战:

 运行结果:

 5.4.4ScheduledThreadPool

根据源码可以看出:
        1. DEFAULT_KEEPALIVE_MILLIS就是默认 10L ,这里就是 10 秒。这个线程池有点像是
        CachedThreadPool和 FixedThreadPool 结合了一下
        2. 不仅设置了核心线程数,最大线程数也是Integer.MAX_VALUE
        3. 这个线程池是上述4 个中唯一一个有延迟执行和周期执行任务的线程池
        4. 适用:周期性执行任务的场景(定期的同步数据)
实战:
    public static void main(String[] args) {
        //带缓存的线程,线程复用,没有核心线程,线程的最大值是 Integer.MAX_VALUE
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        //延迟 n 时间后,执行一次,延迟任务
        executorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("延迟任务执行.....");
            }
        }, 10, TimeUnit.SECONDS);
        //定时任务,固定 N 时间执行一次 ,按照上一次任务的开始执行时间计算下一次任务开始时间
        executorService.scheduleAtFixedRate(() -> {
            System.out.println("定时任务 scheduleAtFixedRate 执行time:" + System.currentTimeMillis());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 1, 1, TimeUnit.SECONDS);
        //定时任务,固定 N 时间执行一次 ,按照上一次任务的结束时间计算下一次任务开始时间
        executorService.scheduleWithFixedDelay(() -> {
            System.out.println("定时任务 scheduleWithFixedDelay 执行time:" + System.currentTimeMillis());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
运行结果:
总结:除了 new ScheduledThreadPool 的内部实现特殊一点之外,其它线程池内部都是基于
ThreadPoolExecutor 类( Executor 的子类)实现的。
5.4.5、自定义ThreadPoolExecutor
    public static void main(String[] args) {
        //核心 4 个 ,最大 10 个 ,30s的空闲销毁非核心6个线程, 队列最大排队 10 个
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 10, 30L, TimeUnit.SECONDS, //超过核心线程数量的线程 30秒之后会退出
                new ArrayBlockingQueue<Runnable>(10), //队列排队10个
                new ThreadPoolExecutor.DiscardPolicy()); //任务满了就丢弃
        for (int i = 0 ; i < 210 ; i++){
            int finalI = i;
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    //始终只有一个线程在执行
                    System.out.println(Thread.currentThread().getName()+":线程执 行..."+ finalI);
                }
            });
        }
    }

分析: 上面示例中,是创建了 210 个线程,但是从结果来看,却只有 10 个线程,就是因为有下面的设置:
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 10,
30L, TimeUnit.SECONDS, //超过核心线程数量的线程 30秒之后会退出
new ArrayBlockingQueue<Runnable>(10), //队列排队10个
new ThreadPoolExecutor.DiscardPolicy()); //任务满了就丢弃
这里设置了最大线程是 10 个,如果多了就会排队 10 个,再多的线程就会直接丢弃

5.5、在ThreadPoolExecutor类中几个重要的方法

Execute :方法实际上是 Executor 中声明的方法,在 ThreadPoolExecutor 进行了具体的实现,这个方法是 ThreadPoolExecutor 的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
Submit :方法是在 ExecutorService 中声明的方法,在 AbstractExecutorService 就已经有了具体的实现,在 ThreadPoolExecutor 中并没有对其进行重写,这个方法也是用来向线程池提交任务的,实际上它 还是调用的 execute() 方法,只不过它利用了 Future 来获取任务执行结果。
Shutdown :不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。
shutdownNow :立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。
isTerminated :调用 ExecutorService.shutdown 方法的时候,线程池不再接收任何新任务,但此时线程池并不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。在调用 shutdown 方法 后我们可以在一个死循环里面用 isTerminated 方法判断是否线程池中的所有线程已经执行完毕,如果子 线程都结束了,我们就可以做关闭流等后续操作了。

5.6、如何设置最大线程数

5.6.1CPU密集型
定义:
CPU 密集型也是指计算密集型,大部分时间用来做计算逻辑判断等 CPU 动作的程序称为 CPU 密集型任务。该类型的任务需要进行大量的计算,主要消耗 CPU 资源。 这种计算密集型任务虽然也可以用多任务 完成,但是任务越多,花在任务切换的时间就越多, CPU 执行任务的效率就越低,所以,要最高效地利 CPU ,计算密集型任务同时进行的数量应当等于 CPU 的核心数。
特点:
1. CPU 使用率较高(也就是经常计算一些复杂的运算,逻辑处理等情况)非常多的情况下使用
2. 针对单台机器,最大线程数一般只需要设置为 CPU 核心数的线程个数就可以了
3. 这一类型多出现在开发中的一些业务复杂计算和逻辑处理过程中。
示例:
    public class Demo02 {
        public static void main(String[] args) {
            //自定义线程池! 工作中只会使用 ThreadPoolExecutor
            /**
             * 最大线程该如何定义(线程池的最大的大小如何设置!)
             * 1、CPU 密集型,几核,就是几,可以保持CPU的效率最高!
             */
            //获取电脑CPU核数
            System.out.println(Runtime.getRuntime().availableProcessors()); //8核
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                    2, //核心线程池大小
                    Runtime.getRuntime().availableProcessors(), //最大核心线程池大小(CPU密集型,根据CPU核数设置)
                    3, //超时了没有人调用就会释放
                    TimeUnit.SECONDS, //超时单位
                    new LinkedBlockingDeque<>(3), //阻塞队列
                    Executors.defaultThreadFactory(), //线程工厂,创建线程的,一般不用动
                    new ThreadPoolExecutor.AbortPolicy()); //银行满了,还有人进来,不处理这个人的,抛出异常
            try {
                //最大承载数,Deque + Max (队列线程数+最大线程数)
                //超出 抛出 RejectedExecutionException 异常
                for (int i = 1; i <= 9; i++) {
                    //使用了线程池之后,使用线程池来创建线程
                    threadPool.execute(() -> {
                        System.out.println(Thread.currentThread().getName() + " ok");
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //线程池用完,程序结束,关闭线程池
                threadPool.shutdown(); //(为确保关闭,将关闭方法放入到finally中)
            }
        }
    }
5.6.2IO密集型
定义:
1 IO 密集型任务指任务需要执行大量的 IO 操作,涉及到网络、磁盘 IO 操作,对 CPU 消耗较少,其消耗的主要资源为 IO
2 、我们所接触到的 IO ,大致可以分成两种:磁盘 IO 和网络 IO
        磁盘 IO ,大多都是一些针对磁盘的读写操作,最常见的就是文件的读写,假如你的数据库、
Redis 也是在本地的话,那么这个也属于磁盘 IO
        网络 IO ,这个应该是大家更加熟悉的,我们会遇到各种网络请求,比如 http 请求、远程数据库读 写、远程 Redis 读写等等。
特点:
        IO 操作的特点就是需要等待,我们请求一些数据,由对方将数据写入缓冲区,在这段时间中,需 要读取数据的线程根本无事可做,因此可以把 CPU 时间片让出去,直到缓冲区写满
        既然这样,IO 密集型任务其实就有很大的优化空间了(毕竟存在等待)
        CPU 使用率较低,程序中会存在大量的 I/O 操作占用时间,导致线程空余时间很多,所以通常就需要开 CPU 核心数两倍的线程。当线程进行 I/O 操作 CPU 空闲时,线程等待时间所占比例越高,就 需要越多线程,启用其他线程继续使用 CPU ,以此提高 CPU 的使用率;线程 CPU 时间所占比例越 高,需要越少的线程,这一类型在开发中主要出现在一些计算业务频繁的逻辑中
示例:
    public class Demo02 {
        public static void main(String[] args) {
            //自定义线程池! 工作中只会使用 ThreadPoolExecutor
            /**
             * 最大线程该如何定义(线程池的最大的大小如何设置!)
             * 2、IO 密集型 >判断你程序中十分耗IO的线程
             * 程序 15个大型任务 io十分占用资源! (最大线程数设置为30)
             * 设置最大线程数为十分耗io资源线程个数的2倍
             */
            //获取电脑CPU核数
            System.out.println(Runtime.getRuntime().availableProcessors()); //8核
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                    2, //核心线程池大小
                    16, //若一个IO密集型程序有15个大型任务且其io十分占用资源!(最大线程数设置为 2*CPU 数目)
                    3, //超时了没有人调用就会释放
                    TimeUnit.SECONDS, //超时单位
                    new LinkedBlockingDeque<>(3), //阻塞队列
                    Executors.defaultThreadFactory(), //线程工厂,创建线程的,一般不用动
            new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试和最早的竞争,也不会抛出异常
            try {
                //最大承载数,Deque + Max (队列线程数+最大线程数)
                //超出 抛出 RejectedExecutionException 异常
                for (int i = 1; i <= 9; i++) {
                    //使用了线程池之后,使用线程池来创建线程
                    threadPool.execute(()->{
                        System.out.println(Thread.currentThread().getName()+" ok");
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //线程池用完,程序结束,关闭线程池
                threadPool.shutdown(); //(为确保关闭,将关闭方法放入到finally中)
            }
        }
    }
         
5.6.3、分析
1 :高并发、任务执行时间短的业务,线程池线程数可以设置为 CPU 核数 +1 ,减少线程上下文的切换
2 并发不高、任务执行时间长的业务这就需要区分开看了:
   a )假如是业务时间长集中在 IO 操作上,也就是 IO 密集型的任务,因为 IO 操作并不占用 CPU ,所以不要让所有的 CPU 闲下来,可以适当加大线程池中的线程数目,让 CPU 处理更多的业务
   b )假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,线程池中的线程数设置得少一些,减少线程上下文的切换(其实从一二可以看出无论并发高不高,对于业务中是否是 cpu 密集还是 I/O 密集的判断都是需要的当前前提是你需要优化性能的前提下)
3 并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些 业务里面某些数据是否能做缓存是第一步,我们的项目使用的时 redis 作为缓存(这类非关系型数据库还 是挺好的)。增加服务器是第二步(一般政府项目的首先,因为不用对项目技术做大改动,求一个稳, 但前提是资金充足),至于线程池的设置,设置参考 2 。最后,业务执行时间长的问题,也可能需要分 析一下,看看能不能使用中间件(任务时间过长的可以考虑拆分逻辑放入队列等操作)对任务进行拆分 和解耦。
5.6.4、总结
1. 一个计算为主的程序( CPU 密集型程序),多线程跑的时候,可以充分利用起所有的 CPU 核心
数,比如说 8 个核心的 CPU , 8 个线程的时候,可以同时跑 8 个线程的运算任务,此时是最大效
率。但是如果线程远远超出 CPU 核心数量,反而会使得任务效率下降,因为频繁的切换线程也是
要消耗时间的。因此对于 CPU 密集型的任务来说,线程数等于 CPU 数是最好的了。
2. 果是一个磁盘或网络为主的程序( IO 密集型程序),一个线程处在 IO 等待的时候,另一个线程还可以在 CPU 里面跑,有时候 CPU 闲着没事干,所有的线程都在等着 IO ,这时候他们就是同时的 了,而单线程的话此时还是在一个一个等待的。我们都知道 IO 的速度比起 CPU 来是很慢的。此时线程数等于 CPU 核心数的两倍是最佳的。

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

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

相关文章

Java的XWPFTemplate word生成列表

Java的XWPFTemplate工具类导出word.docx的使用_xwpftemplate 语法_youmdt的博客-CSDN博客 如果是表格的列表参考上面这篇文章即可&#xff0c;比较复杂的列表遍历暂时还没找到方法&#xff0c;只能手动创建表格了 上面是模板&#xff0c;非常简单&#xff0c;以为我们是要自己创…

高效免费办公神器——ONLYOFFICE入手指南

前言&#xff1a; 作为开发者&#xff0c;有时候经常为寻找适合的开发工具而苦恼&#xff1b;或者因为高昂的费用而犹豫不决&#xff1b;亦或喜欢的办公产品只能在单一的平台上使用&#xff0c;与其把时间花在复杂的工具使用上&#xff0c;不如节省出时间投入思考和技术的提升。…

云课五分钟-01课程在哪里-无需安装网页直达

此部分课程均为2015-2019年规划和设计&#xff0c;2020-2022年新版课程还在内测中。 现在想想当年还是很莽的&#xff0c;总想着一个网页云服务&#xff0c;把机器人相关不涉及硬件的课程全囊括。 无需安装个性定制即开即用随时随地云端复现…… 视频 云课五分钟-01课程在哪…

C++设计实现日志系统

转载&#xff1a;C设计实现日志系统 - 知乎 (zhihu.com) 日志系统几乎是每一个实际的软件项目从开发、测试到交付&#xff0c;再到后期的维护过程中极为重要的 查看软件代码运行流程、 还原错误现场、 记录运行错误位置及上下文等的重要依据。一个高性能的日志系统&#xff0c…

【ArcGIS Pro微课1000例】0032:创建具有指定高程Z值的矢量数据

本文讲解ArcGIS Pro中创建具有指定高程值的矢量数据的两种方法。 文章目录 一、独立创建1. 新建地图场景2. 新建shapefile3. 绘制多边形4. 添加高程字段5. 三维显示二、基于高程源创建1. 创建栅格范围2. 添加Z值字段3. 添加Z信息4. 要素更新Z值一、独立创建 1. 新建地图场景 …

Pytorch多GPU并行训练: DistributedDataParallel

1 模型并行化训练 1.1 为什么要并行训练 在训练大型数据集或者很大的模型时一块GPU很难放下&#xff0c;例如最初的AlexNet就是在两块GPU上计算的。并行计算一般采取两个策略&#xff1a;一个是模型并行&#xff0c;一个是数据并行。左图中是将模型的不同部分放在不同GPU上进…

【vue】AntDV组件库中a-upload实现文件上传:

文章目录 一、文档&#xff1a;二、使用(以Jeecg为例)&#xff1a;【1】template&#xff1a;【2】script&#xff1a; 三、效果图&#xff1a; 一、文档&#xff1a; Upload 上传–Ant Design Vue 二、使用(以Jeecg为例)&#xff1a; 【1】template&#xff1a; <a-uploa…

Springboot项目返回数据统一封装

Springboot项目返回数据统一封装,支持swagger。 正常swagger会根据数据库表的注释显示对应的参数释义等。但当我们使用统一接口返回map时&#xff0c;部分注释等信息会被掩盖消失。在此提供三个java类即可满足统一封装返回接口&#xff0c;也可显示对应的swagger释义等。 1.Er…

Vue 2学习(路由、history 和 hash 模式、)-day014

一、路由简介 路由&#xff08;route&#xff09;就是一组 key-value 的对应关系多个路由&#xff0c;需要经过路由器&#xff08;router&#xff09;的管理 在 Vue 中也有路由&#xff0c;Vue 中的路由主要是通过 vue-rounter 这个插件库来实现&#xff0c;它的作用就是专门用…

php 插入排序算法实现

插入排序是一种简单直观的排序算法&#xff0c;它的基本思想是将一个数据序列分为有序区和无序区&#xff0c;每次从无序区选择一个元素插入到有序区的合适位置&#xff0c;直到整个序列有序为止 5, 3, 8, 2, 0, 1 HP中可以使用以下代码实现插入排序算法&#xff1a; functi…

【考研复习】二叉树的特殊存储|三叉链表存储二叉树、一维数组存储二叉树、线索二叉树

文章目录 三叉链表存储二叉树三叉链表的前序遍历&#xff08;不使用栈&#xff09;法一三叉链表的前序遍历&#xff08;不使用栈&#xff09;法二 一维数组存储二叉树一维数组存储二叉树的先序遍历 线索二叉树的建立真题演练 三叉链表存储二叉树 三叉链表结构体表示如下图所示…

2023-11-15 LeetCode每日一题(K 个元素的最大和)

2023-11-15每日一题 一、题目编号 2656. K 个元素的最大和二、题目链接 点击跳转到题目位置 三、题目描述 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。你需要执行以下操作 恰好 k 次&#xff0c;最大化你的得分&#xff1a; 从 nums 中选择一个元素 m 。将选中…

三极管工作原理介绍(动画图解)

三极管BJT 各位社区的小伙伴们大家好&#xff0c;相信大家也像我一样&#xff0c;因为上学的时候学过&#xff0c;但是呢&#xff0c;出社会不怎么用&#xff0c;久而久之就忘了&#xff0c;今天来给大家巩固一下&#xff0c;我对三极管的通俗易懂的工作原理介绍&#xff01; 半…

计算机视觉+深度学习+机器学习+opencv+目标检测跟踪+一站式学习(代码+视频+PPT)

第1章&#xff1a;视觉项目资料介绍与学习指南 相关知识&#xff1a; 介绍计算机视觉、OpenCV库&#xff0c;以及课程的整体结构。学习概要&#xff1a; 了解课程的目标和学习路径&#xff0c;为后续章节做好准备。重要性&#xff1a; 提供学生对整个课程的整体认识&#xff0…

Python 如何实现组合(Composite)设计模式?什么是组合设计模式?

什么是组合&#xff08;Composite&#xff09;设计模式&#xff1f; 组合&#xff08;Composite&#xff09;设计模式是一种结构型设计模式&#xff0c;它允许客户端使用单一对象和组合对象&#xff08;对象的组合形成树形结构&#xff09;同样的方式处理。这样&#xff0c;客…

c++之xml的创建,增删改查

c之xml的创建&#xff0c;增删改查 1.创建写入2.添加3.删除4.修改&#xff1a; 1.创建写入 #include <stdio.h> #include <typeinfo> #include "F:/EDGE/tinyxml/tinyxml.h" #include <iostream> #include <string> #include <Winsock2.…

王道数据结构课后代码题p40 6.有一个带头结点的单链表L,设计一个算法使其元素递增有序 (c语言代码实现)

这一题其实用到了直接插入排序的思想 视频讲解在这里哦&#xff1a;&#x1f447; p40 第6题 王道数据结构课后代码题 c语言代码实现_哔哩哔哩_bilibili 本题代码为 void paixu(linklist* L)//对单链表内的元素排序 {lnode* p (*L)->next;lnode* pre *L;lnode* r p-&…

Skybox天空盒子的更换教程_unity基础开发教程

Skybox天空盒子的更换 Skybox的下载与导入更换SkyboxSkybox属性自定义 Skybox的下载与导入 打开资源商店 搜索FREE Skybox 这里是我使用的是这一款资源&#xff0c;点击添加至我的资源 打开包管理器Package Manager Packages选择My Assets 搜索Sky 选择刚刚添加的天空盒子 点…

3D Gaussian Splatting文件的压缩【3D高斯泼溅】

在上一篇文章中&#xff0c;我开始研究高斯泼溅&#xff08;3DGS&#xff1a;3D Gaussian Splatting&#xff09;。 它的问题之一是数据集并不小。 渲染图看起来不错。 但“自行车”、“卡车”、“花园”数据集分别是一个 1.42GB、0.59GB、1.35GB 的 PLY 文件。 它们几乎按原样…

西门子精彩触摸屏SMART LINE V4 面板使用U盘下载项目程序的具体方法示例

西门子精彩触摸屏SMART LINE V4 面板使用U盘下载项目程序的具体方法示例 WinCC flexible SMART V4 SP1 软件针对SMART LINE V4 面板新增了使用U盘下载项目功能。 注意:“使用U盘下载项目”功能仅支持触摸屏OS版本为V4.0.1.0 及以上的设备。 使用U盘下载项目的步骤可参考以下内…