线程池-ThreadPoolExecutor

2023-05-16

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?在Java中可以通过线程池来达到这样的效果。

为什么要线程池

多线程技术主要解决处理器单元内多个线程执行的问题,可以减少处理器单元的闲置时间,增加处理器单元吞吐能力,多线程的情况下确实可以最大限度发挥多核处理器的计算能能力,但是如果随意使用线程池,对系统性能反而不利

  • 创建和销毁线程是需要时间:假如一个服务器完成一项任务所需要的时间为:T1表示线程创建时间,T2表示线程执行任务时间,T3表示销毁时间,如果T1+T3远大于T2会得不偿失
  • 线程也需要占用内存空闲,大量的线程会抢占宝贵的内存资源,可能会导致OOM的异常
  • 大量的线程回收也会给GC带来很大的压力,延长GC停顿的时间
  • 大量的线程也会抢占CPU资源,CPU不停的在各个线程上下文切换,反而没有时间去处理线程运行的时候该处理的任务

什么是线程池

线程池就是实现创建若干的可执行的线程放入一个池中,需要的时候从池中获取线程,不用自行创建,使用完成不需要销毁线程而放入线程池中, 从而减少创建和销毁对象的开销

因此通过池资源来避免频繁的创建和销毁线程,让创建的线程进行复用,就有了线程池的概念,线程池里会维护一部分活跃的线程,如果有需要,就去线程池中取线程使用,用完归还到线程池中,免去创建和销毁线程的开销,且线程池也会对线程的数量有一定的限制,线程池的本质是对线程资源的复用。

线程池的优势:

1、降低资源的消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗

2、提高响应速度,当任务到达时,任务可以不需要等待线程创建就可以立即执行

3、提高了线程的可管理性,不仅可以降低系统资源消耗,提高了系统稳定性

4、线程池可以实现统一的分配,调优和监控

线程池的架构

Executor接口是最基础的接口

ExecutorService接口继承了Executor,在其上添加了一些扩展方法,可以说是真正的线程池接口

AbstractExecutorService抽象类实现了ExecutorService中的大部分的接口

ThreadPoolExecutor继承了AbstractExecutorService,是线程池的具体实现

ScheduledExecutorService接口继承自ExecutorService接口,提供了"周期性执行"的功能

ScheduledThreadPoolExecutor类即继承自ThreadPoolExecutor由实现ScheduledExecutorService接口,是”带有周期性执行“功能的线程池

Executors是线程池的静态工厂,提供了快速创建线程池的静态方法

Executor接口:

提交任务接口,只提供了一个execute方法,执行Runable类型任务

public interface Executor {
    void execute(Runnable command);
}

ExecutorService接口

public interface ExecutorService extends Executor {
    //关闭线程池
    void shutdown();
    //立即关闭
    List<Runnable> shutdownNow();
    //是否关闭
    boolean isShutdown();
    //是否终止
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
    //提交任务 ,提交任务类型多样,还具有异步功能
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

ExecutorService接口是真正线程池的接口,在Executor基础上做了一些扩展,主要是提交任务,终止任务

其中Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。
Future提供了三种功能:

  • 判断任务是否完成
  • 能够中断任务
  • 能够获取任务执行的结果

向线程池中提交任务的submit方法不是阻塞方法,而Future.get方法是一个阻塞方法,当submit提交多个任务时,只有所有任务都完成后,才能使用get按照任务的提交顺序得到返回结果,所以一般需要使用future.isDone先判断任务是否全部执行完成,完成后再使用future.get得到结果。 

Future接口定义了主要的5个接口方法,有RunnableFuture和SchedualFuture继承这个接口,以及CompleteFuture和ForkJoinTask继承这个接口。

 ScheduledExecutorService接口

ScheduledExecutorService接口提供了具有周期性执行任务的方法,其继承了ExecutorService接口。

public interface ScheduledExecutorService extends ExecutorService {
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);

    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

}

Executors创建常见的线程池

Executor工厂类提供线程初始化接口,主要有如下几种

newFixedThreadPool-固定数量的线程池

public class testFixedThreadPool {
    private static AtomicInteger num = new AtomicInteger();
    public static void main(String[] args) {
        //固定数量的线程池
        ExecutorService newThread = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 20; i++) {
            newThread.submit(new Runnable() {
                @Override
                public void run() {
                    Random random = new Random();
                    System.out.println("线程:"+Thread.currentThread().getName()+",执行任务序号:"+num.getAndIncrement());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

 固定线程数量的线程池实现:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

 包括核心线程、最大线程、等待时间、时间单位以及阻塞队列中的LinkedBlockingQueue。

创建一个指定工作线程数的线程池,其中corepoolsize和maxnumPoolSize相等,阻塞队列是基于LinkedBlockingQueue实现的

FixedThreadPool:传入的核心线程数是固定的,所有称为有界线程池,最大线程数和核心线程数相等。

假设核心线程是3,一次性提交20个任务,先启动了3个线程执行3个任务,剩下17个任务进入阻塞队列,因为核心线程和最大线程数相等,所以keepAlivetime参数没有意义,等待任一线程执行结束就会继续从等待队列中获取一个任务进行执行,其优点在于固定线程数量的线程池可以提供程序效率和节省创建线程所消耗的时间开销。

newCachedThreadPool-可缓存工作线程线程池

public class testCachedThreadPool {
    private static AtomicInteger num = new AtomicInteger();
    public static void main(String[] args) {
       //可缓存工作线程的线程池
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            cachedThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    Random random = new Random();
                    System.out.println("线程:"+Thread.currentThread().getName()+",执行任务序号:"+num.getAndIncrement());
                    try {
                        Thread.sleep(random.nextInt(100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

 具体实现:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

创建一个可缓存的工作线程池,核心线程是0,线程池中线程数可达Integer.MAX_VALUE,即2^32,线程默认存活时间60秒,内部使用的队列SynchronousQueue同步阻塞队列,在没有任务执行时,当线程空闲时间超过60秒,则工作线程将会终止,当提交新任务是,如果没有空闲线程,则创建新线程执行任务可缓存线程至,如果有新的任务且没有可用的空闲线程,则新建线程,如果长时间线程空闲(60s)则对线程进行回收,

此线程池不会线程池大小做线程,线程池大小完全依赖于操作系统能够创建的最大线程大小。

可缓存线程池应用场景适合耗时短,不需要考虑同步的场合

 newSingleThreadExecutor:单个线程的线程池

public class testSingleThreadExecutor {
    private static AtomicInteger num = new AtomicInteger();
    public static void main(String[] args) {
        //固定数量的线程池
        ExecutorService newThread = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 20; i++) {
            newThread.submit(new Runnable() {
                @Override
                public void run() {
                    Random random = new Random();
                    System.out.println("线程:"+Thread.currentThread().getName()+",执行任务序号:"+num.getAndIncrement());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

 底层实现

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

单线程线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的一个线程可以保证提交任务的顺序执行,内部使用LinkedBlockingQueue作为阻塞队列

newFixedThreadPool(1):固定数量的线程的线程池在给定参数为1的情况下就可以看做是newSingleThreadExecutor的同等实现

newScheduledThreadPool:周期性执行任务线程池

public class DIYRunnable implements Runnable{
    private Integer num;

    public DIYRunnable(Integer num) {
        this.num = num;
    }

    @Override
    public void run() {
        System.out.println("线程名:"+Thread.currentThread().getName()+":执行任务编号:"+num+":当前时间:"+System.currentTimeMillis());
    }
}
public class testSchedule {
    public static void main(String[] args) {
        //周期性执行任务的线程池
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
        for(int i = 0; i < 3; i++) {
            scheduledExecutorService.scheduleWithFixedDelay(
                    new DIYRunnable(i),2,2,TimeUnit.SECONDS);
        }
    }
}

 底层实现:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

阿里手册说到线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

Executors 返回的线程池对象的弊端如下:

  • FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
  • CachedThreadPool 和 ScheduledThreadPool : 允许的创建线程数量Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

ScheduledThreadPoolExecutor:周期性线程池


/**
* 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期
* 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推
* 如果执行任务发生异常,随后的任务将被禁止,否则任务只会在被取消或者Executor被终止后停止
* 如果任何执行的任务超过了周期,随后的执行会延时,不会并发执行
*/
public ScheduledFuture<?> scheduleAtFixedRate(
    Runnable command,
    //初始延时
    long initialDelay,
    //间隔时间
    long period,
    //时间单位
    TimeUnit unit);

/**
* 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟
* 如果执行任务发生异常,随后的任务将被禁止,否则任务只会在被取消或者Executor被终止后停止
*/
public ScheduledFuture<?> scheduleWithFixedDelay(
    Runnable command,
    long initialDelay,
    long delay,
    TimeUnit unit);

scheduleAtFixedRate:该方法在initialDelay时长后第一次执行任务,以后每隔period时长,再次执行任务。注意,period是从任务开始执行算起的。开始执行任务后,定时器每隔period时长检查该任务是否完成,如果完成则再次启动任务,否则等该任务结束后才再次启动任务

scheduleWithFixDelay:该方法在initialDelay时长后第一次执行任务,以后每当任务执行完成后,等待delay时长,再次执行任务

ThreadPoolExecutor结构

ThreadPoolExecutor构造参数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

其中几个重要参数的解释如下:

corePoolSize:核心线程数量

线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize,如果当前线程数为corePoolSize,继续提交的任务会被存放到阻塞队列中,等待被执行

maximumPoolSize:最大线程数量

线程池中允许的最大的线程数,如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前的线程小于maximumPoolSize

keepAliveTime:空闲线程存活时间

线程空闲时存活时间,即当线程没有任务执行时,线程会继续存活keepAliveTime,结合TimeUnit给定时间单位,默认情况下,该参数只有线程数大于corePoolSize时才有用

workQueue:阻塞队列

workQueue必须是BlockingQueue的阻塞队列,当线程池中的线程数超过corePoolSize的时候,线程会进入阻塞队列进行等待,通过workQueue线程池实现了阻塞功能

几种阻塞队列:

同步阻塞队列:SyschronousQueue:提交的任务直接提交给线程而不保存它

无界阻塞队列:LinkedBlockingQueue:基于链表实现的有界队列,可以存放无限多的提交的任务

有界阻塞队列:ArrayBlockingQueue:基于数组实现的有界队列,指定对了的最大长度,防止资源耗尽。

threadFactory:线程工厂

创建线程的工厂,通过自定义的线程公共可以给每个新建的线程设置一个具有识别度的线程名:默认线程名:poll-X-Therad-XX

实现ThreadFactory接口,接口声明如下:

public interface ThreadFactory {
    Thread newThread(Runnable r);
}

RejectedExecutionHandler:饱和策略

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略

AbortPolicy:直接抛出异常,默认策略

CallerRunsPolicy:用调用者所在的线程来执行任务

DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并将当前任务添加到队列中

DiscardPolicy:直接丢弃任务

也可以自定义饱和策略执行,RejectedExecutionHandler

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程池-ThreadPoolExecutor 的相关文章

  • Spring-IOC容器进行对象管理

    目录 IOC概念 IOC思想 Spring管理对象 集成依赖 spring的配置文件 xff08 Applicationcontext xml xff09 创建实体类User Spring对Bean的实例化方式 基于配置形式 1 通过无参构
  • Spring-AOP原理及实现

    Spring AOP AOP Aspect Oriented Programing 面向切面编程 xff1a 扩展功能不通过修改源代码实现 AOP采用横向抽取机制 xff0c 取代传统纵向继承体系实现响应的功能 xff08 性能监控 事务
  • Spring&Mybatis整合及Spring中JDBCTemplate的使用

    Spring和Mybatis整合 在mybatis中 xff0c 操作数据库需要获取到SQLSession对象 xff0c 而该对象的实例过程在mybatis是通过SQLSessionFactoryBuilder读取全局配置文件来实例化一个
  • SpringMVC设计模式

    什么是MVC MVC是模型 Model 视图 View 控制器 Controller 的简写 xff0c 是一种软件设计规范 是将业务逻辑 数据 显示分离的方法来组织代码 MVC主要作用是降低了视图与业务逻辑间的双向偶合 MVC不是一种设计
  • SSM框架整合

    整合思路 主要分为Controller xff0c service层 xff0c dao层 整合dao mybatis和spring的整合 xff0c 通过spring来管理mapper接口 xff0c 数据源 xff0c 使用mapper
  • SSM框架实战-搭建自己的个人博客1-基础架构搭建

    前言 本系列文章主要通过从零开始搭建自己的个人博客 xff0c 来加深对SSM框架的学习与使用 xff0c 了解一个系统从提出到设计 到开发 到测试 部署运行的过程 xff0c 并记录在搭建过程中的学习心得 遇见的错误及解决方式 代码放在g
  • SSM框架实战-搭建自己的个人博客2-UEditor编辑器的使用

    目录 UEditor 博客内容提交与展示功能测试 Controller开发 新增博客页面add ueditor jsp 博客详情界面detail jsp 博客新增和展示详情功能开发 博客存储 博客标题开发 标签POJO类 TagMapper
  • SSM框架实战-搭建自己的个人博客3-登录实现及前端界面设计

    目录 后台登录功能 前端页面 后端开发 前端界面设计 详情首页 js脚本 SSM整体设计 Dao层 Service层 Mapper xml Controller 子博文界面 部署至服务器 后台登录功能 登录页面 xff1a 用户名和密码 通
  • 超分辨率重建-PNSR与SSIM的计算(RGB、YUV和YCbCr互转)

    RGB YUV和YCbCr 自己复现的网络跑出来的模型进行预测的时候 xff0c 不知道为啥算出来的结果比论文中要低好多 不论scale factor为多少 xff0c 我算出来的结果均普遍低于论文中给出的 xff0c PSNR大概低个1
  • 如何写简历

    注意点 xff1a 篇幅 校招一页 社招二页 谨慎使用精通 精通 gt 熟悉 xff08 推荐使用 xff09 gt 掌握 xff08 推荐使用 xff09 gt 了解 xff08 推荐使用 xff09 拿不准的不要写在简历上 突出自己技能
  • SSM框架实战-搭建自己的个人博客4-文章管理与展示

    实现功能 主要实现上图所示的功能 xff0c 从数据库中查询到所有文章数据 xff0c 并进行显示如标题 xff0c 栏目等信息 xff0c 可以通过分类查询文章 xff0c 通过标签查询文章 xff0c 也可以通过搜索进行模糊查询 xff
  • Pytorch加载与保存模型(利用pth的参数更新h5预训练模型)

    前言 以前用Keras用惯了 xff0c fit和fit generator真的太好使了 xff0c 模型断电保存搞个checkpoint回调函数就行了 近期使用pytorch进行训练 xff0c 苦于没有类似的回调函数 xff0c 写完网
  • 如何用pyplot优雅的绘制loss,acc,miou,psnr变化曲线

    前言 TensorFlowBoard过于强大 xff0c 导致我对它依赖性很强 xff0c 今年转手使用pytorch进行开发 xff0c 本以为没了TensorFlowBoard xff0c 后来发现人家Tensorflow封装了个Ten
  • Pytorch实现CA,SA,SE注意力机制

    通道注意力CA class ChannelAttention nn Module def init self in planes ratio 61 16 super ChannelAttention self init self avg p
  • Python使用OpenCV按自定义帧率提取视频帧并保存

    在做室外语义分割 视觉导航与定位的时候 xff0c 通常会用对一个连续的视频帧进行测试 xff0c 除去常用数据集外 xff0c 也经常会自己制作一些数据集 xff0c 这个工具类可以按需求对视频进行分帧提取 xff0c 封装好了直接可以使
  • 悲观锁与乐观锁详解

    悲观锁 悲观锁顾名思义是从悲观的角度去思考问题 xff0c 解决问题 它总是会假设当前情况是最坏的情况 xff0c 在每次去拿数据的时候 xff0c 都会认为数据会被别人改变 xff0c 因此在每次进行拿数据操作的时候都会加锁 xff0c
  • 亚像素卷积网络(ESPCN)学习与Pytorch复现

    论文内容 论文地址 xff1a Real Time Single Image and Video Super Resolution Using an Efficient Sub Pixel Convolutional Neural Netw
  • Lock锁和ReentrantLock锁

    前言 JDK 1 5中提供的锁的接口java util concurrent locks Lock xff0c 其提供了一些ReentrantLock ReentrantReadWriteLock实现类 参考JDK文档 xff1a Java
  • 面试题--JVM垃圾回收及内存管理

    选择题 1 以下哪些内存区域属于 JVM 规范 xff1f xff08 xff09 A 方法区 B 实例变量 C 静态变量 D 程序计数器 E 虚拟机栈 正确答案 xff1a A D E 解析 xff1a Java虚拟机规范划分了七个内存区
  • Pytorch维度操作-unsqueeze、squeeze、view与permute

    view 在pytorch中view函数的作用为重构张量的维度 相当于numpy中resize 的功能 a 61 1 2 3 b 61 2 3 4 c 61 3 5 5 d 61 4 5 6 e 61 np array a b c d e

随机推荐

  • 长假余额为零!我用Python做了个中秋国庆双节拼图游戏

    点击上方 菜鸟学Python xff0c 选择 星标 公众号 重磅干货 xff0c 第一时间送达 今年的国庆长假非常长 xff0c 不知不觉已经余额为零 xff01 朋友圈很多晒出游的照片 xff0c 聚会的照片 xff0c 吃吃喝喝真舒服
  • Redis系列学习1-Redis安装启动与基础概念

    Remote Dictionary Server Redis 是一个由 Salvatore Sanfilippo 写的 key value 存储系统 xff0c 是跨平台的非关系型数据库 Redis 是一个开源的使用 ANSI C 语言编写
  • Redis系列学习2-五大类型(String,List,Hash,Set,ZSet)及其常规操作

    Redis的基本操作 Redis默认是有16个数据库 xff0c 默认使用的是第0个数据库 xff0c 可以通过select 切换数据库 xff0c Redis的命令大小写不敏感的 切换数据库 切换数据库 格式 xff1a select i
  • Redis系列学习3-geospatial地理空间

    geospatial 地理空间 可以用来实现定位 附近的人 打车APP上距离计算 距离的实现主要基于经纬度 xff0c 城市的经纬度查询 xff1a http www jsons cn lngcode geoadd 添加地址位置 格式 xf
  • 遗传算法求解TSP旅行商问题

    旅行商问题 旅行商问题 traveling salesman problem TSP 可描述为 已知N个城市之间的相互距离 现有一个商人必须遍访这N个城市 并且每个城市只能访问一次 最后又必须返回出发城市 如何安排他对这些城市的访问次序 使
  • 剑指Offer-面试算法题

    1 二分查找 xff08 递归与非递归实现 xff09 基本算法 xff0c 掌握好循环条件 package com company Description 二分查找 xff08 递归与非递归实现 xff09 Created by Wanb
  • Python爬虫-抓取PC端网易云音乐评论(GUI界面)

    歌曲搜素 网易云音乐网址为 xff1a https music 163 com 思路是进入后输入一个歌曲名 xff0c 点击搜索按钮 xff0c 通过开发者调试工具捕获搜索请求 xff0c 捕获到的数据信息如下 xff1a 所有的歌曲相关信
  • Package cmake is not available, but is referred to by another package.

    inux环境下安装Cmake报错 xff1a Package cmake is not available but is referred to by another package This may mean that the packa
  • 完美数问题

    题目描述 对于一个十进制正整数 xff0c 如果z的每一位数字只可能是1 2 3中的其中一个 xff0c 则称 是完美数 如 123 1 3321都是完美数而5 1234则不是 牛牛想写一个函数f n xff0c 使得其返回最大的不大于n的
  • 围圈抽牌报数问题

    问题描述 米免参加公司司建 xff0c 100个同事围坐圈 xff0c 裁判开始顺时针从头发牌 xff0c 每发3张白牌就会发出1张黑 牌 xff0c 抽到黑牌的人出局 xff0c 每局第N个抽到黑牌的将获得奖励 问如果米免想获得奖品 xf
  • RTX30系列-Ubuntu系统配置与深度学习环境Pytorch配置

    本文完成RTX3090Windows 43 Ubuntu双系统配置 xff0c 并配置深度学习环境 硬件环境为RTX3090 43 Z590主板 xff0c 64GB RAM xff0c 2TB固态 xff0c 8TB存储 Ubuntu系统
  • 【rotors】多旋翼无人机仿真(一)——搭建rotors仿真环境

    rotors 多旋翼无人机仿真 xff08 一 xff09 搭建rotors仿真环境 rotors 多旋翼无人机仿真 xff08 二 xff09 设置飞行轨迹 rotors 多旋翼无人机仿真 xff08 三 xff09 SE3控制 roto
  • JVM内存管理

    JVM内存管理 Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域 xff0c JDK 1 8 和之前的版本的数据区域有所差异 xff0c JDK1 6如下图所示 图片来源 xff1a JavaGuid
  • AQS、Semaphore、CountDownLatch与CyclicBarrier原理及使用方法

    AQS AQS 的全称为 AbstractQueuedSynchronizer xff0c 翻译过来的意思就是抽象队列同步器 这个类在 java util concurrent locks 包下面 xff0c AQS 就是一个抽象类 xff
  • 滑动窗口框架算法

    最长覆盖子串 xff0c 异位词 xff0c 最长无重复子串等等许多子串问题用常规暴力法费时费力 xff0c 一些大佬的解法虽然很强效率很高 xff0c 但是太难想到了 xff0c 这类问题用滑动窗口算法解决非常的快捷简便 滑动窗口算法思想
  • Python-深度学习常用脚本

    记录一些因为在网络训练 xff0c 测试过程中经常用到的一些脚本 1 视频按帧提取 可以从一段视频中截取不同帧的图片 xff0c 并保存至文件夹 需要自己更改视频路径和图片保存路径 import os import cv2 import s
  • Java面试基础(一)

    1 重载与重写 重载就是同样的一个方法能够根据输入数据的不同 xff0c 做出不同的处理 重写就是当子类继承自父类的相同方法 xff0c 输入数据一样 xff0c 但要做出有别于父类的响应时 xff0c 你就要覆盖父类方法不同类型的对象 x
  • 网络篇-传输控制协议TCP

    TCP协议 传输控制协议 xff08 TCP xff0c Transmission Control Protocol xff09 用一句话概括的话 xff0c 它是一种面向连接的 可靠的 基于字节流的传输层通信协议 TCP xff08 传输
  • 阻塞队列-BlockingQueue

    对于Queue而言 xff0c BlockingQueue是主要的线程安全的版本 xff0c 具有阻塞功能 xff0c 可以允许添加 删除元素被阻塞 xff0c 直到成功为止 xff0c blockingqueue相对于Queue而言增加了
  • 线程池-ThreadPoolExecutor

    如果并发的线程数量很多 xff0c 并且每个线程都是执行一个时间很短的任务就结束了 xff0c 这样频繁创建线程就会大大降低系统的效率 xff0c 因为频繁创建线程和销毁线程需要时间 那么有没有一种办法使得线程可以复用 xff0c 就是执行