ThreadPoolExecutor原理解析
1. 工作原理
1.1 流程图
1.2 执行示意图
从上图得知
如果当前运行的线程数小于corePoolSize(核心线程数),则会创建新线程作为核心线程来执行任务(注意,执行这一步需要获取全局锁)。
如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue中。
如果无法将任务加入BlockingQueue(队列已满),则将创建线程来执行任务(注意,执行这一步骤需要获取全局锁)。
如果第三步创建线程使得当前线程数大于maximumPoolSize,任务将被拒绝,并调用RejectdExecutionHandler.rejectedExecution()方法。
2. 源码解析
2.1 核心参数
corePoolSize 核心线程数,池中所保存的线程数,包括空闲线程。
maximumPoolSize 线程池最大容量,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)
workQueue 用于保存任务的队列
SynchronousQueue 线性安全、capacity是0不存储任何元素、可以在两个线程中传递同一个对象。synchronousQueue.put(object);synchronousQueue.take();如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。
LinkedBlockingQueue 链式队列,队列容量不足或为0时自动阻塞。锁是分离的,高并发的情况下生产者和消费者可以并行地操作队列中的数据,可以提高吞吐量。当QPS很高,发送数据很大,大量的任务被添加到这个无界LinkedBlockingQueue 中,导致cpu和内存飙升服务器挂掉。
ArrayBlockingQueue 数组实现的有界队列,使用独占锁,生产者和消费者无法并行操作,在高并发场景下会成为性能瓶颈。
PriorityBlockingQueue PriorityBlockingQueue中的优先级由任务的Comparator决定。
keepAliveTime 空闲线程的存活时间 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize。如果allowCoreThreadTimeout=true,则会直到线程数量=0。
threadFactory 线程工厂 默认DefaultThreadFactory 创建同样分组(ThreadGroup) 同样优先级(NORM_PRIORITY) non-daemon的线程。
handler 拒绝策略处理 默认AbortPolicy 抛异常RejectedExecutionException
AbortPolicy:不执行该任务,并抛出RejectedExecutionException异常。
CallerRunsPolicy:由调用线程处理该任务,如果执行程序已关闭,则会丢弃该任务。
DiscardOldestPolicy:丢弃队列中最老的一个任务,然后重新尝试执行任务(重复此过程。
DiscardPolicy:不执行该任务,也不抛异常。
2.2 线程池状态
2.2.1 线程池状态
成员变量ctl是Integer的原子变量,使用一个变量同时记录线程池状态和线程池中线程个数,假设计算机硬件的Integer类型是32位二进制标示,如下面代码所示,其中高3位用来表示线程池状态,后面29位用来记录线程池线程个数。
//线程个数掩码位数,并不是所有平台int类型是32位,所以准确说是具体平台下Integer的二进制位数-3后的剩余位数才是线程的个数
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程最大个数(低29位)00011111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//(高3位):11100000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
//(高3位):00000000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
//(高3位):00100000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
//(高3位):01000000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
//(高3位):01100000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
获取线程池的运行状态和线程池中的线程个数
// 获取高三位 运行状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//获取低29位 线程个数
private static int workerCountOf(int c) { return c & CAPACITY; }
//计算ctl新值,线程状态 与 线程个数
private static int ctlOf(int rs, int wc) { return rs | wc; }
2.2.2 线程池状态含义:
RUNNING:接收新任务并且处理阻塞队列里的任务。
SHUTDOWN:拒绝新任务但是处理阻塞队列里的任务。
STOP:拒绝新任务并且抛弃阻塞队列里的任务,同时中断正在处理的任务。
TIDYING:所有任务都执行完(包含阻塞队列里面任务),当前线程池活动线程为0,将要调用terminated方法。
TERMINATED:终止状态。terminated方法调用完成以后的状态。
2.3 执行方法
2.3.1 线程池中任务提交方法
execute()方法
public void execute(Runnable command) {//(1) 如果任务为null,则抛出NPE异常if (command == null)throw new NullPointerException();//(2)获取当前线程池的状态+线程个数变量的组合值int c = ctl.get();//(3)当前线程池线程个数是否小于corePoolSize,小于则开启新线程运行if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}//(4)如果线程池处于RUNNING状态,则添加任务到阻塞队列if (isRunning(c) && workQueue.offer(command)) {//(4.1)二次检查int recheck = ctl.get();//(4.2)如果当前线程池状态不是RUNNING则从队列删除任务,并执行拒绝策略if (! isRunning(recheck) && remove(command))reject(command);//(4.3)如果当前线程池线程为空,则添加一个线程else if (workerCountOf(recheck) == 0)addWorker(null, false);}//(5)如果队列满了,则新增线程,新增失败则执行拒绝策略else if (!addWorker(command, false))reject(command);
}
submit(Runnable task)方法
public Future<?> submit(Runnable task) {// 6 NPE判断if (task == null) throw new NullPointerException();// 7 包装任务为FutureTaskRunnableFuture<Void> ftask = newTaskFor(task, null);// 8 投递到线程池执行execute(ftask);// 9 返回ftaskreturn ftask;
}protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {return new FutureTask<T>(runnable, value);
}public FutureTask(Runnable runnable, V result) {//将runnable适配为Callable类型任务,并且让result作为执行结果this.callable = Executors.callable(runnable, result);this.state = NEW; // ensure visibility of callable
}
submit(Runnable task,T result)方法
public <T> Future<T> submit(Callable<T> task) {if (task == null) throw new NullPointerException();RunnableFuture<T> ftask = newTaskFor(task);execute(ftask);return ftask;
}protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {return new FutureTask<T>(callable);
}
2.3.2 线程池中任务执行方法
当用户线程提交任务到线程池后,在线程池没有执行拒绝策略的情况下,用户线 程会马上返回,而提交的任务要么直接切换到线程池中的Worker线程来执行,要么先放入线程池的阻塞队列里面,稍后再由Worker线程来执行。
Worker(Runnable firstTask) {// 在调用runWorker前禁止中断 避免当前Worker在调用runWorker方法前被中断(当其他线程调用了线程池的shutdownNow时,如果Worker状态≥0则会中断该线程)。status=0标示锁未被获取的状态,state=1标示锁已经被获取的状态,state=–1是创建Worker时默认的状态。setState(-1);this.firstTask = firstTask;//使用线程池中指定的线程池工厂创建一个线程作为该Worker对象的执行线程this.thread = getThreadFactory().newThread(this);
}public void run() {runWorker(this);//委托给runWorker方法
}final void runWorker(Worker w) {Thread wt = Thread.currentThread();Runnable task = w.firstTask;w.firstTask = null;//(1)status设置为0,允许中断。这时候调用shutdownNow会中断Worker线程。w.unlock();boolean completedAbruptly = true;try {//(2)当前task==null或者调用getTask从任务队列获取的任务返回nullwhile (task != null || (task = getTask()) != null) {//(2.1)获取工作线程内部持有的独占锁w.lock();...try {//(2.2)任务执行前干一些事情beforeExecute(wt, task);Throwable thrown = null;try {//(2.3)执行任务task.run();} catch (RuntimeException x) {thrown = x; throw x;} catch (Error x) {thrown = x; throw x;} catch (Throwable x) {thrown = x; throw new Error(x);} finally {//(2.4)任务执行完毕后干一些事情afterExecute(task, thrown);}} finally {task = null;//(2.5)统计当前Worker完成了多少个任务w.completedTasks++;w.unlock();}}completedAbruptly = false;} finally {//(3)执行清工作processWorkerExit(w, completedAbruptly);}
}private void processWorkerExit(Worker w, boolean completedAbruptly) {...//(3.1)统计整个线程池完成的任务个数,并从工作集里面删除当前woker 加全局锁final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {completedTaskCount += w.completedTasks;workers.remove(w);} finally {mainLock.unlock();}//(3.2)如果当前线程池状态是shutdown状态并且工作队列为空,或者当前是stop状态并且当前线程池里面没有活动线程,则设置线程池状态为TERMINATED。tryTerminate();//(3.3)判断当前线程中的线程个数是否小于核心线程个数,如果是则新增一个线程。int c = ctl.get();if (runStateLessThan(c, STOP)) {if (!completedAbruptly) {int min = allowCoreThreadTimeOut ? 0 : corePoolSize;if (min == 0 && ! workQueue.isEmpty())min = 1;if (workerCountOf(c) >= min)return; // replacement not needed}addWorker(null, false);}
}
2.3.3 线程池关闭方法
shutdown()方法
调用 shutdown后,线程池就不会再接收新的任务,但是工作队列里面的任务还是要执行的,该方法是立刻返回的,并不同步等待队列任务完成再返回。
首先使用CAS设置当前线程池状态为TIDYING,如果成功则执行扩展接口terminated在线程池状态变为TERMINATED前做一些事情,然后设置当前 线程池状态为TERMINATED,最后调用termination.signalAll()来激活调用线程池的 awaitTermination系列方法被阻塞的所有线程。
public void shutdown() {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {//(1)权限检查如果设置了安全管理器,则看当前调用shutdown命令的线程是否有关闭线程的权限,如果有权限则还要看调用线程是否有中断工作线程的权限,如果没有权限则抛出SecurityException或者NullPointerException异常。checkShutdownAccess();//(2)设置当前线程池状态为SHUTDOWN,如果已经是SHUTDOWN则直接返回advanceRunState(SHUTDOWN);//(3)设置中断标志。这里首先加了全局锁,同时只有一个线程可以调用shutdown设置中断标志。然后尝试获取Worker本身的锁,获取成功则设置中断标识,由于正在执行的任务已经获取了锁,所以正在执行的任务没有被中断。这里中断的是阻塞到getTask()方法,企图从队列里获取任务的线程,也就是空闲线程。interruptIdleWorkers();onShutdown();} finally {mainLock.unlock();}//(4)尝试状态变为TERMINATEDtryTerminate();
}private void advanceRunState(int targetState) {for (;;) {int c = ctl.get();if (runStateAtLeast(c, targetState) ||ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))break;}
}private void interruptIdleWorkers(boolean onlyOne) {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {for (Worker w : workers) {Thread t = w.thread;//如果工作线程没有被中断,并且没有正在运行则设置中断if (!t.isInterrupted() && w.tryLock()) {try {t.interrupt();} catch (SecurityException ignore) {} finally {w.unlock();}}if (onlyOne)break;}} finally {mainLock.unlock();}
}final void tryTerminate() {for (;;) {
...int c = ctl.get();
...final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {//设置当前线程池状态为TIDYINGif (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {try {terminated();} finally {//设置当前线程池状态为TERMINATEDctl.set(ctlOf(TERMINATED, 0));//激活调用条件变量termination的await系列方法被阻塞的所有线程termination.signalAll();}return;}} finally {mainLock.unlock();}}
}
shutdownNow()方法
调用 shutdownnow后,线程池就不会再接收新的任务,并且会丢弃工作队列里面的任务,正在执行的任务也会被中断,该方法是立刻返回的,并不同步等待激活的任务执行完成再返回。
调用线程池队列的drainTo方法把队列中的任务移除到taskList 里,如果发现线程池队列还不为空(比如DelayQueue或者其他类型的队列drainTo可能 移除元素失败),则循环移除里面的元素,最后返回移除的任务列表。
public List<Runnable> shutdownNow() {List<Runnable> tasks;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {//(5)权限检查checkShutdownAccess();//(6) 设置线程池状态为stopadvanceRunState(STOP);//(7)中断所有线程,这里需要注意的是中断所有线程,包含空闲线程和正在执行任务的线程interruptWorkers();//(8)移动队列任务到taskstasks = drainQueue();} finally {mainLock.unlock();}//(9)终止状态tryTerminate();return tasks;
}private void interruptWorkers() {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {for (Worker w : workers)w.interruptIfStarted();} finally {mainLock.unlock();}
}private List<Runnable> drainQueue() {//8.1获取任务队列BlockingQueue<Runnable> q = workQueue;ArrayList<Runnable> taskList = new ArrayList<Runnable>();//8.2 从任务队列移除任务到taskList列表q.drainTo(taskList);//8.3 如果q还不为空,则说明drainTo接口调用失效,则循环移除if (!q.isEmpty()) {for (Runnable r : q.toArray(new Runnable[0])) {if (q.remove(r))taskList.add(r);}}//8.4返回异常的任务列表return taskList;
}
2.4 参数优化
2.4.1 默认值
corePoolSize = 1
maxPoolSize = Integer.MAX_VALUE
queueCapacity = Integer.MAX_VALUE
keepAliveTime = 60s
allowCoreThreadTimeout = false
rejectedExecutionHandler = AbortPolicy()
需要根据几个值来决定
tasks :每秒的任务数,假设为500~1000
taskcost:每个任务花费时间,假设为0.1s
responsetime:系统允许容忍的最大响应时间,假设为1s
corePoolSize
corePoolSize = 每秒需要多少个线程处理?
threadcount = tasks/(1/taskcost) = taskstaskcout = (500 ~ 1000)0.1 = 50~100 个线程。
corePoolSize设置应该大于50。根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80。
queueCapacity
队列容量 queueCapacity = (coreSizePool/taskcost)*responsetime
切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,
不能新开线程来执行,响应时间会随之陡增。
maxPoolSize
最大线程数在生产环境上我们往往设置成corePoolSize一样,这样可以减少在处理过程中创建线程的开销。
rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理。
keepAliveTime和allowCoreThreadTimeout采用默认通常能满足。
2.4.2 参考值
@Configuration
public class ConcurrentThreadGlobalConfig {@Beanpublic ThreadPoolTaskExecutor defaultThreadPool() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心线程数目executor.setCorePoolSize(65);//指定最大线程数executor.setMaxPoolSize(65);//队列中最大的数目executor.setQueueCapacity(650);//线程名称前缀executor.setThreadNamePrefix("DefaultThreadPool_");//rejection-policy:当pool已经达到max size的时候,如何处理新任务//CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行//对拒绝task的处理策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//线程空闲后的最大存活时间executor.setKeepAliveSeconds(60);//加载executor.initialize();return executor;}
}
以上
相关文章:
ThreadPoolExecutor原理解析
1. 工作原理1.1 流程图1.2 执行示意图从上图得知如果当前运行的线程数小于corePoolSize(核心线程数),则会创建新线程作为核心线程来执行任务(注意,执行这一步需要获取全局锁)。如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQue…...
谷粒学苑第二章前端框架-2.2前端框架开发过程
一、前端框架开发过程 第一步:添加路由 src/router模块用来管理路由。 第二步:点击某个路由,显示路由对应页面内容 component: () > import(/views/table/index), 表示路由对应的页面,是views/table/index.vue页面 第三步&a…...
权限管理实现的两种方式(详解)
登录的接口请求的三个内容:1. token2. 用户信息、角色信息3. 菜单信息第一种:基于角色Role的动态路由管理 (不推荐,但市场用的比较多)首先列出枚举每个角色对应几个路由,然后根据用户登录的角色遍历枚举出来的角色动态注册对应的路…...
【C++】智能指针思路解析和模拟实现
此篇文章就从以下几个方面出发,带你了解智能指针的方方面面1.为什么需要智能指针当我们开辟内存并使用的时候,我们的顺序应该是这样:开辟内存-》使用内存-》释放内存问题就出现在第三步,开辟好了,也使用了,…...
SpringCloud(18):Sentinel流控降级入门
Sentinel本地应用流控降级实现分为三步: 创建本地应用搭建本地Sentinel控制台本地应用接入本地Sentinel控制台1 本地应用创建 整体流程分析 创建springboot项目在项目的pom.xml文件中引入sentinel-core的依赖坐标创建TestController,定义使用限流规则运行测试具体流程 1.创…...
C++【多态】
文章目录1、多态的概念2、多态的定义及实现2-1、多态的构成条件2-2、虚函数2-3、虚函数的重写2-4 多态样例2-5、协变2-6、 析构函数与virtual2-7、函数重载、函数隐藏(重定义)与虚函数重写(覆盖)的对比2-8、override 和 final&…...
缓存预热、缓存雪崩、缓存击穿、缓存穿透,你真的了解吗?
缓存穿透、缓存击穿、缓存雪崩有什么区别,该如何解决? 1.缓存预热 1.1 问题描述 请求数量较高,大量的请求过来之后都需要去从缓存中获取数据,但是缓存中又没有,此时从数据库中查找数据然后将数据再存入缓存…...
【Java基础】018 -- 面向对象阶段项目上(拼图小游戏)
目录 拼图小游戏(GUI) 一、主界面分析 1、练习一:创建主界面1 2、练习二:创建主界面2(JFrame) 3、练习三:在游戏界面中添加菜单(JMenuBar) ①、菜单的制作 4、添加图片&a…...
【网络~】
网络一级目录二、socket套接字三、UDP数据报套接字四、TCP流套接字一级目录 1.局域网、广域网 2.IP地址是什么? IP地址是标识主机在网络上的地址 IP地址是如何组成的? 点分十进制,将32位分为四个部分,每个部分一个字节ÿ…...
手写JavaScript中的call、bind、apply方法
手写JavaScript中的call、bind、apply方法 call方法 call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。 function Product(name, price) {this.name name;this.price price; }function Food(name, price) {Product.call(this, name, price);t…...
JAVA练习46-将有序数组转换为二叉搜索树
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 前言 提示:这里可以添加本文要记录的大概内容: 2月10日练习内容 提示:以下是本篇文章正文内容,下面案例可供参考 一、题目-…...
linux(centos7.6)docker
官方文档:https://docs.docker.com/engine/install/centos/1安装之前删除旧版本的docker2安装yum install-y yum-utils3配置yum源 不用官网的外国下载太慢 推荐阿里云yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.r…...
微信小程序滚动穿透问题
文章目录1、catchtouchmove"true"2、page-meta3、wx.setPageStyle做小程序的开发业务中,经常会使用弹窗,当弹窗里的内容过多时,要滚动查看,然后经常会遇到滚动弹窗,弹窗底下页面也跟着滚。解决思路ÿ…...
安全—06day
负载均衡反向代理下的webshell上传负载均衡负载均衡下webshell上传的四大难点难点一:需要在每一台节点的相同位置上传相同内容的webshell难点二:无法预测下一次请求是哪一台机器去执行难点三:当我们需要上传一些工具时,麻烦来了&a…...
PostgreSQL入门
PostgreSQL入门 简介 PostgreSQL是以加州大学伯克利分校计算机系开发的POSTGRES, 版本 4.2为基础的对象关系型数据库管理系统(ORDBMS) 支持大部分SQL标准并且提供了许多现代特性 复杂查询外键触发器可更新视图事务完整性多版本并发控制 …...
自媒体人都在用的免费音效素材网站
视频剪辑、自媒体人必备的剪辑音效素材网站,免费下载,建议收藏! 1、菜鸟图库 音效素材下载_mp3音效大全 - 菜鸟图库 菜鸟图库是一个综合性素材网站,站内涵盖设计、图片、办公、视频、音效等素材。其中音效素材就有上千首…...
Java数据结构中二叉树的深度解析及常见OJ题
本篇文章讲述Java数据结构中关于二叉树相关知识及常见的二叉树OJ题做法讲解(包含非递归遍历二叉树) 目录 一、二叉树 1.1二叉树概念 1.2特殊的二叉树 1.3二叉树性质 1.4二叉树基本性质定理题 1.5二叉树遍历基本操作 1.6二叉树遍历的前中后非递归写法 1.7…...
算法顶级比赛汇总
可参赛的算法比赛 阿里云天池大数据竞赛 时间:每年各个季度很多类型都会出题(比赛总时间大概为两个月) 内容:各个类型的算法题都会出、奖金上万不等 形式:在线提交(提交后在线检查结果)、离线…...
Android MVI框架搭建与使用
MVI框架搭建与使用前言正文一、创建项目① 配置AndroidManifest.xml② 配置app的build.gradle二、网络请求① 生成数据类② 接口类③ 网络请求工具类三、意图与状态① 创建意图② 创建状态四、ViewModel① 创建存储库② 创建ViewModel③ 创建ViewModel工厂五、UI① 列表适配器②…...
第九节 使用设备树实现RGB 灯驱动
通过上一小节的学习,我们已经能够编写简单的设备树节点,并且使用常用的of 函数从设备树中获取我们想要的节点资源。这一小节我们带领大家使用设备树编写一个简单的RGB 灯驱动程序,加深对设备树的理解。 实验说明 本节实验使用到STM32MP1 开…...
Ubuntu 系统下Docker安装与使用
Ubuntu 系统下Docker安装与使用Docker安装与使用Docker安装安装环境准备工作系统要求卸载旧版本Ubuntu 14.04 可选内核模块Ubuntu 16.04 使用 APT 安装安装 Docker CE使用脚本自动安装启动 Docker CE建立 docker 用户组测试 Docker 是否安装正确镜像加速Docker使用拉取镜像创建…...
DHCP安全及防范
DHCP安全及防范DHCP面临的威胁DHCP饿死攻击仿冒DHCP Server攻击DHCP中间人攻击DHCP Snooping技术的出现DHCP Snooping防饿死攻击DHCP Snooping防止仿冒DHCP Server攻击DHCP Snooping防止中间人攻击DHCP Snooping防止仿冒DHCP报文攻击DHCP面临的威胁 网络攻击无处不在ÿ…...
【流畅的python】第一章 Python数据模型
文章目录第一章 Python 数据模型1.1 python风格的纸牌1.2 如何使用特殊方法-通过创建一个向量类的例子1.3 特殊方法汇总第一章 Python 数据模型 python最好的品质是一致性 python解释器碰到特殊句法时,会使用特殊方法去激活一些基本的对象操作 这些特殊的方法以两个…...
from文件突然全部变为类cs右击无法显示设计界面
右击也不显示查看设计器 工程文件 .csproj中将 <Compile Include"OperatorWindows\Connection.cs" /> <Compile Include"OperatorWindows\Connection.Designer.cs"> <DependentUpon>Connection.cs</DependentUpon> &…...
使用arthas中vmtool命令查看spring容器中对象的某个属性
场景: 线上环境我想查看spring中容器某个对象的属性值 vmtool命令 方式一: vmtool --action getInstances -c [类加载器的hash] --className [目标类全路径] --limit 10 -x 2 实例:查询该类的全部属性情况(该类是一个spri…...
四种幂等性解决方案
什么是幂等性? 幂等是一个数学与计算机学概念,在数学中某一元运算为幂等时,其作用在任一元素两次后会和其作用一次的结果相同。 在计算机中编程中,一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。 幂等…...
【Nacos】Nacos配置中心客户端配置更新源码分析
上文我们说了服务启动的时候从远程Nacos服务端拉取配置,这节我们来说下Nacos服务端配置的变动怎么实时通知到客户端,首先需要注册监听器。 注册监听器 NacosContextRefresher类会监听应用启动发布的ApplicationReadyEvent事件,然后进行配置…...
按钮防抖与节流-vue2
防抖与节流,应用场景有很多,例如:禁止重复提交数据的场景、搜索框输入搜索条件,待输入停止后再开始搜索。 防抖 点击button按钮,设置定时器,在规定的时间内再次点击会重置定时器重新计时,在规定…...
PyTorch学习笔记:nn.SmoothL1Loss——平滑L1损失
PyTorch学习笔记:nn.SmoothL1Loss——平滑L1损失 torch.nn.SmoothL1Loss(size_averageNone, reduceNone, reductionmean, beta1.0)功能:创建一个平滑后的L1L_1L1损失函数,即Smooth L1: l(x,y)L{l1,…,lN}Tl(x,y)L\{l_1,\dots,l…...
2年时间,涨薪20k,想拿高薪还真不能老老实实的工作...
2016年开始了我的测试生活。 2016年刚到公司的时候,我做的是测试工程师。做测试工程师是我对自己的职业规划。说实话,我能得到这份工作真的很高兴。 来公司的第一个星期,因为有一个项目缺人,所以部门经理提前结束了我的考核期&a…...
dreamweaver绿色版下载/seo搜索引擎优化推荐
本文刚刚发在51CTO.com网站,文章链接:[url]http://soft.51cto.com/art/200611/34788.htm[/url]11月中旬,CA EXPO 2006分别在上海和北京召开。作为一直与CA保持联系的记者,51CTO老杨受邀参与北京站的会议。我是2000年开始接触和了解…...
南宁网站建设公司哪家专业/中国新冠疫情最新消息
✨个人主页:bit me👇 ✨当前专栏:Java EE初阶👇 目 录你是怎么理解乐观锁和悲观锁的,具体怎么实现呢? 悲观锁认为多个线程访问同一个共享变量冲突的概率较大, 会在每次访问共享变量之前都去真正加锁. 乐观…...
开发商交房必备条件/seo交流论坛
之前下载vc运行库都是百度,从中关村、当下等软件网站下载,但是最近这些网站涉及到安全问题,所以从官网下载比较合适 微软官网-中文 在搜索中 搜索vc2010/2015等,搜索结果中找到xxxxxxxxxx Redistributable Package (x64)这种Redis…...
wordpress文章关联/网盘搜索神器
4个类就可体验HADOOP-RPC的简单、实用。 一,writable-rpc 一,协议 所谓协议,实际是一个接口,用来定义该RPC提供的功能 package rpc.writeable;public interface MyProtocol {void mkdir(String path);String getName(String na…...
云存储wordpress/google搜索
CSS背景属性 属性描述值IEFNW3Cbackground简写属性,作用是将背景属性设置在一个声明中。 background-color background-image background-repeat background-attachment background-position 4161background-attachment设置是否背景图像是固定的或随页面其余部分滚动…...
番禺建设网站外包/东莞百度搜索网站排名
2019独角兽企业重金招聘Python工程师标准>>> 腰椎间盘突出我是L45号,左腿发麻有时疼痛,腰开始只是酸痛,因为是在外地打工怕家里担心,有胆小,周末百度了下就去了医院。还好当时着的是三家公立医院࿰…...