- 博客(63)
- 收藏
- 关注
原创 【设计模式】简单理解策略模式
作者是未踏入职场的小白,写此篇博客的目的旨在加深自己的策略模式的理解,同时期望能让不了解策略模式的读者有一个简单的认知。日常生活中,有很多种支付方式:微信、支付宝、银行、现金。System.out.println("微信支付...");System.out.println("支付宝支付...");System.out.println("银行卡支付...");}else{System.out.println("现金支付...");
2024-01-28 21:09:14 411
原创 【Java】equals和hashCode需要一起重写的原因
是native方法,不由Java实现,而是在运行时通过本地调用实现(默认是根据对象的内存地址生成),同时在文档中此方法有一个很重要的规范:If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result(),但这里两个相同对象(
2024-01-16 19:37:04 920
原创 【Java】子线程获取父线程ThreadLocal
阅读这篇博客前,请确保你对ThreadLocal的使用场景和实现原理有一定了解。【Java】ThreadLocal原理与使用场景-CSDN博客。本文主要聚焦于ThreadLocal在父子线程间传递的原理。
2024-01-10 16:37:32 1885 1
原创 【Java】LockSupport原理与使用
每次调用park方法都会消耗一个permit。当permit数量为1个时,再次调用unpark方法不会让permit继续累加,这意味着permit的最大值为1,最小值为0。该方法的作用也是使当前线程阻塞,与无参的park相比多了一个锁对象:blocker,如果我们想让线程阻塞于某一对象,以便我们更好的了解和管理线程的阻塞状态就可以使用该方法。测试结果表明其它线程可以提前调用unpark方法,让permit数量变成1,当既定线程调用park方法时,由于permit数量不为0所以不会陷入阻塞,而是立即返回。
2024-01-05 20:23:50 1379
原创 【Java】ThreadLocal原理与使用场景
但我们知道,ThreadLocal真正操作的是每个线程内部的ThreadLocalMap,ThreadLocal对象只作为key存储到每个线程自己的ThreadLocalMap中,我们可以通过key找到value,实现线程间的隔离。ThreadLocal作为静态字段存在于UserHolder类中,UserHolder的set方法、get方法实际上都是由ThreadLocal实现,这里使用到了组合的思想。线程调用ThreadLocal对象的set方法时,会获取当前线程的ThreadLocalMap对象。
2023-12-31 19:52:17 1013
原创 【Redis】bigKey、hotKey问题
俗称“大key”,一般value的体积较大(Hash、String数据结构)、或是集合中含有的元素数量过多(List、ZSet数据结构)的key都是大key。以常用的数据结构为例:String类型的Key,它的值为5MB(value的体积较大)。Hash类型的Key,如果它的成员数量只有1000个,但这些成员的value总大小为100MB,那么这个key就是bigKey(成员的value体积过大)。List类型的Key,它的成员数量为20000个(元素数量过多)。ZSe。
2023-12-30 19:57:53 641
原创 【集合】Vector与CopyOnWriteArrayList
此篇博客着重于:在多线程并发执行读、写操作的场景下Vector集合CopyOnWriteArrayList集合是否能保证线程安全?它们是通过什么方式保证线程安全的?
2023-12-26 18:27:39 977
原创 【IO】IO模型与零拷贝
正在运行的程序其实就是系统中的一个进程,操作系统会为每一个进程分配内存空间,而内存空间分为两部分,一部分是用户空间,这是用户进程访问的内存区域;另一部分是内核空间,是操作系统内核访问的内存区域。如网络、磁盘IO等操作,出于安全性考虑,用户进程不能直接与外部设备进行数据交互,用户进程只能向操作系统发起IO调用请求,由操作系统内核与外部设备进行数据交互,完成真正的IO操作。
2023-12-24 17:27:37 1529
原创 【并发】保证共享变量在多线程并发时的线程安全
此时线程Adder-1又获得了CPU的时间片,能够继续执行赋值操作,将静态变量i的值修改成2,这就相当于将线程Adder-2之前三次操作的结果给覆盖掉了,这也是为什么最终结果总是小于预期值的原因。依照上图,假设静态变量i的值一开始是1,线程Adder-1先获取了静态变量i的值,执行+1操作,准备赋值给静态变量i时,线程Adder-1的CPU时间片用完了,线程被挂起;随后线程Adder-2也来执行相同的操作,由于它的CPU时间片充足,它能完整的执行所有操作。为什么会出现这种情况呢?将静态变量i的实现改为。
2023-12-22 15:14:04 945
原创 【代码规范】统一参数校验、结果返回
表示字符串的开头,1表示手机号码的开头数字是1,[3-9]表示第二位数字是3到9之间的数字,\d表示匹配一个数字,{9}表示匹配9个数字,$表示字符串的结尾。在编写业务层组件实现真正的业务处理逻辑前,需要编写一大堆的参数校验逻辑。可以看到有些接口返回String字符串,有些接口返回数据传输对象(xxxDto),接口返回给前端的结果不统一,导致前端不能高效的处理响应数据。枚举类ProcessResult包含了响应状态码、处理结果描述等信息,我们如果需要多个不同的响应码及响应信息,可直接在枚举类中扩展。
2023-12-21 21:50:41 866
原创 【诊断】linux系统下的内存溢出问题定位
1、通过top命令定位异常进程(pid)。2、通过top -H -p pid命令查看异常进程下,线程CPU使用率情况。3、通过jstack pid > log.txt命令,将线程的堆栈信息打印到log.txt文件中。4、通过cat log.txt | grep tid(thread id) -A 20命令,过滤出异常线程的相关信息。5、通过jstat -gcutil pid 2000 10命令,查看堆内存使用情况。6、
2023-12-20 12:53:57 1291
原创 【可用性】Redis作为注册中心配合Spring Task的高可用案例
1、每个服务在启动时都会通过UUID生成随机字符串作为自己的唯一标识,随后基于Hash结构将每个服务的唯一标识和时间戳存储在redis中。2、服务上报(保活)实际上就是不断修改value时间戳,以此来表示服务仍可用。3、服务检查会对所有保存在Hash结构中的服务节点进行检查,判断上报时间是否在规定范围内,没有及时上报的服务节点会从Hash结构中剔除。4、方案缺陷是每个服务间的系统时钟不能偏差太多,否则会存在误判,将一些正常服务从redis中剔除。
2023-12-18 19:38:24 1398
原创 【系统架构】集群、分布式概念及系统架构演进过程
对食物没有太高要求的人在肚子饿的时候一般都会选择去兰州拉面、沙县小吃等小饭馆,这类小饭馆有个很显著的特点:洗菜、切菜、炒菜都是同一个人完成,如果厨子不舒服可能饭馆还会歇业。而一些人流量较大的饭馆的分工则不像小饭馆那样“随便”,厨房中可能有多个洗菜的、多个切菜的、多个炒菜的,每个人都各司其职,就算其中一个炒菜的大厨生病了也不会导致饭馆歇业,顶多影响一下上菜速度。 在上述例子中,我们可以将洗菜、切菜、炒菜视作一个独立的模块,小饭馆中的大厨同时需要干这三件事可以抽象成多个功能模块部署在一个项目中、小
2023-12-14 22:23:35 740
原创 【Spring】手写一个简易starter
自定义一个starter,里面包含一个@TimeLog注解和一个TimeLogAspect切面类,用于统计接口耗时。要求在其它项目引入starter依赖后,启动springboot项目时能进行自动装配。 (1)引入pom依赖: (2)定义注解@TimeLog: @Target:注解作用“对象”,这里声明注解作用在方法上。 @Retention:表示注解在运行时会被保留,可以通过反射机制读取。 (3)定义切面类TimeLogAs
2023-12-12 12:05:43 197
原创 【Spring】@SpringBootApplication注解解析
当我们第一次创建一个springboot工程时,我们会对启动类(xxxApplication)有许多困惑,为什么只要运行启动类我们在项目中自定义的bean无需配置类配置,扫描就能自动注入到IOC容器中?为什么我们在pom文件中引入starter就可以自动的将第三方组件注册到IOC容器中?这些困惑的答案就是本文的答案。
2023-12-11 20:12:18 256
原创 【异常】浅析异常体系及为什么一定会执行finally块
(1)所有异常(Exception)、错误(Error)都继承自异常中的基类:Throwable。而异常又可以分为检查异常(Checked Exception)、非检查异常(Unchecked Exception)两大类。 (2)检查异常:在编译期间由编译器检查的异常,编译器确保这些异常在编译期被处理,意味着不能直接使用关键字throw抛出异常,要么使用try、catch处理异常,要么在方法声明上使用throws关键字提醒方法调用者该方法可能会抛出的异常,让方法调用者处理异常。Exceptio
2023-12-09 13:28:31 109
原创 【Spring】依赖注入之属性注入详解
我们在进行web开发时,基本上一个接口对应一个实现类,比如IOrderService接口对应一个OrderServiceImpl实现类,给OrderServiceImpl标注@Service注解后,Spring在启动时就会将其注册成bean进行统一管理。在Controller层需要使用到Service层的服务组件时,就通过或等注解标注接口,Spring会自动为我们注入接口的实现类。在IOrderService接口只有一个实现类:OrderServiceImpl时,这么写当然没有问题。
2023-12-06 23:22:38 449 1
原创 【二分答案法】Leetcode相关题目解析
2、nums[m-1] > nums[m],这里呈下降趋势,而索引0到索引1是上升趋势,所以在0 到 m - 1这个区间肯定有一个峰值元素,right = m - 1往左侧二分;3、nums[m+1] > nums[m],这里呈上升趋势,而索引n - 2到索引n - 1是下降趋势,所以在m + 1 到 n - 2这个区间肯定有一个峰值元素,left = m + 1往右侧二分。据题知,索引-1、索引n(n为数组长度)处的元素都默认为无穷小,我们可以在一开始特判一些简单情况:当数组长度等于1时,直接返回0;
2023-12-06 18:49:07 174
原创 【序列化】概念及二叉树序列化、反序列化的两种方式
(1)进程想要运行,就要向操作系统申请内存空间,进程对数据的所有操作都是在内存空间中完成的。内存中有一部分数据很重要,我们希望将这些数据存储到磁盘中,就算进程被杀死、机器重启仍能找到这些数据,这时候就需要使用到序列化。(2)以redis执行命令的流程为例,客户端(进程)想要执行一条命令,首先需要将命令封装(序列化)成redis协议指定的数据格式,随后基于socket将数据发送给redis服务(进程);
2023-12-05 15:51:39 70
原创 【异常】捕获线程池执行任务时产生的异常
在编写程序时,我们为了充分利用多核CPU、加快接口响应速度,通常会使用线程池来处理请求,但线程池执行任务过程中难免会出现异常,导致请求失败。那如果我们想在任务发生异常后捕获异常,并做一些”善后“操作,该如何做呢?
2023-12-03 22:58:49 650
原创 【数据结构】二叉树遍历的非递归实现
本文使用栈以非递归的形式遍历整颗二叉树,我是通过数组模拟栈来实现的,如果对用数组模拟栈不太熟悉,你可以直接使用Stack类作为栈实现。
2023-12-03 18:16:49 321 1
原创 【程序设计】简易生产者、消费者模型
创建消息队列时需要指定队列的容量上限,队列中没有消息时,消费者从队列中take元素会阻塞;队列中的消息数量达到容量上限时,生产者往队列中put元素会阻塞。设置队列容量上限为3,4个生产者同时生产一条消息放入队列,2s后,1个消费者来消费消息,看生产者线程在消费者take消息前是否会阻塞。先启动消费者线程,过2s后再启动生产者线程,看消费者线程是否会在生产者put消息前一直阻塞。
2023-12-02 17:45:04 62
原创 【Java】浅析FutureTask的核心方法get
一个线程想要运行,首先它得知道它的任务是什么,而这两个接口恰好是用于表示一个线程该执行的任务。Runnable和Callable两个接口都是任务接口,它们之间有何不同呢?Runnable中的run方法是没有返回值的,而Callable中的call方法有返回值V(泛型),一般情况下我们都会使用Runnable接口,当需要线程的执行结果时就使用Callable接口。那么我们如何获取一个线程的执行结果呢?此时就需要用到Future接口及其实现类FutureTask了。
2023-12-01 17:26:23 684
原创 【分布式唯一id】产生原因及生成方案
例如,每次从数据库获取 id 时,获取一个号段,如[1,2999],这个范围表示3000 个 ID,业务应用在请求获取 id 时,只需要在本地从 1 开始自增并返回(注意线程安全问题,使用原子类来做自增操作),而不用每次去请求数据库,一直到本地自增到 2999 时,再去数据库重新获取新的号段,后续流程循环往复。分布式ID生成系统需要具有较高的可用性,因为业务数据的唯一标识都由该系统分配,若系统不可用,将直接影响用户、订单这些强依赖于ID生成系统的模块。如果id乱序,将不利于数据库的写入和排序操作。
2023-11-30 16:08:58 283
原创 【QuickSort】单边快排思路及实现
(1)首先明确base case:当l >= r时,数组不需要进行排序。(2)每次排序确定一个索引位置p,p左边都是小于它的元素,p右边都是大于它的元素,它不再参与排序。(3)索引位置p确定后,需要排序就只剩下区间:[l,p - 1]、[p + 1,r],不断递归,直至l >= r时,排序结束。
2023-11-28 16:59:45 228
原创 【二叉树】常见题目解析
以示例树为例,以7为根节点的两颗子树(假设左子树为p、右子树为q),当二叉树轴对称时,总是有p.left = q.right、p.right = q.left,那我们可不可以把问题转换成:给定两个节点p、q(分别是根节点的左节点和右节点),判断这两个节点是否相等,若相等,再判断p.left = q.right、p.right = q.left是否成立,成立则对称,不成立则不对称。如给定的示例树,先到达根节点5翻转7、9两个子节点,再遍历7、9两个子节点让他们各自翻转自己的子节点,如同逐层进行翻转。
2023-11-24 16:51:13 35
原创 【Springboot】基于AOP和自定义注解实现权限校验
(1)假设现在有一个项目,用户只需登录就可以访问所有接口。基于这种场景,我们一般的权限鉴权逻辑是:用户登录后生成一个随机token保存到redis并返回给用户端,用户随后的每次请求都会携带这个token。服务器配置拦截器拦截用户请求,查看请求是否携带token并且查询token是否有效,若请求没有携带token或者token过期,直接拒绝请求,若token有效则刷新token的过期时间,放行请求。(2)
2023-11-23 16:16:30 856
原创 【算法】01背包问题解析
01背包问题的题目描述一般是:给你一个正数m,代表背包的容量。此时有n个物品,每个物品都有自己的体积costs[i]和价值values[i],每个物品只能选择一次。问在不超过背包容量m的情况下,让你尽可能的往背包里放物品,你能让背包装载的最大价值是多少?(1)解决01背包问题一般用二维dp数组,那么dp[i][j]的含义是:对于前i个物品,背包容量为j的情况下,背包所能装载的最大价值是dp[i][j]。(2)
2023-11-22 16:32:32 61
原创 【数据结构】位图的概念与实现
在Java中,整型(int)的最大值MAX_VALUE = 2^31 - 1。而整型数组(int [ ])的下标是从0开始的,也就意味着一个整型数组最多存放2^31 - 1个元素。这种存储方式优点很明显:无论元素大小(不超过整型范围),每个元素所占的空间都是32位的,可以存下很大的数字;缺点是空间浪费的现象比较明显。位图(Bitset)是一种在整型数组的基础上充分利用空间的一种数据结构。其实就是利用bit位的取值来“存放值”,bit位是1代表元素存在、bit位是0代表元素不存在。
2023-11-20 15:59:53 378
原创 【MySQL】linux系统下的慢SQL定位与执行计划分析
根据字面意思,慢是指这条SQL语句执行时间过长。那么执行时间究竟多长才能定义成慢呢?(1)这个变量代表是否开启慢查询日志,开启则会将所有慢查询SQL记录到文件中,值为1则表示开启,0表示关闭(默认是关闭的,需要手动打开)。查看slow_query_log的初始值:重启mysql服务,再次查看slow_query_log的值:(2)这个变量代表查询SQL执行时间的“底线”,执行时间超过这个预设值就会定义为慢SQL并被记录到mysql-slow.log文件中(单位是s)。
2023-11-19 16:27:04 289
原创 【Spring事务】简述声明式事务与@Transactional注解
在Spring中,有两种方式实现事务:一是编程式事务,通过或等事务管理器,在业务代码中手动地开启事务,并根据业务代码执行情况进行对应的提交事务或回滚事务操作;二是声明式事务,通过在业务方法上标注注解,由Spring来帮我们执行事务管理操作。事务传播行为指事务方法B在被事务方法A调用时,是选择另启一个新事务,在新事务中运行、还是加入事务方法A开启的事务并在其中运行、抑或是抛出异常,事务方法B的表现形式就是事务传播行为。(1)
2023-11-17 15:10:24 41
原创 【负载均衡】概念及实现方式
如今的服务为了支持更大的用户流量,基本上都采用多节点集群的方式部署。那么用户请求的路由策略就需要我们进行把控,不能让多个用户的请求都打到同一个服务节点上(这样与单节点部署没区别),而是让多个用户请求平均的打到集群中的每一个节点,充分利用集群部署的优势,加快请求处理速度,提高用户满意度。负载均衡指将外部请求均匀地分配给每一个服务节点,接收到请求的服务节点独立地响应用户请求,避免多个请求打到一个服务节点导致负载过高的现象产生。通过负载均衡服务器能快速响应结果给用户,应对大量并发访问的场景。
2023-11-13 20:21:32 54
原创 【算法】记忆化搜索与动态规划
题目要求一条从(0,0)到(m,n)的路径,这条路径上所有的数字和要最小。那么对于(i,j)这一点,它的最小路径和与其左边的点(i,j - 1)和其上边的点(i - 1,j)有关,因为题目规定只能往右或者往下走。先把简单格子的最小路径和算好(比如第一行和第一列的所有格子),再从左往右逐行计算每个格子的最小路径和,每个格子只会计算一次,dp数组充当了备忘录的角色。那么求到(i,j)这一点的最小路径和不就变成了求(i,j - 1)和(i - 1,j)这两点的最小路径和吗?
2023-11-11 17:42:14 78
原创 【ThreadPoolExecutor】浅析线程池构造方法的主要参数
使用该阻塞队列作为线程池的任务队列时,maximumPoolSize参数将会失效,因为当核心线程都在处理任务时,若有新任务被提交,新任务会进入队列中,显然,任务量不可能超过整数最大值,因此也不会有救急线程被创建。在使用该队列时,我们要确保线程池处理任务的速度大于新任务到达的平均速度,否则会有内存溢出的风险。当线程池中线程的数量大于核心线程的数量时,多于的空闲线程等待keepAliveTime时间后,如果还没有新的任务到来,那么这些线程将会终止。线程池最多能容纳的线程数(核心线程+救急线程)。
2023-11-09 16:43:42 55
原创 【设计模式】JDK中的动态代理
假设现在有一个需求,让你统计每个订单接口的耗时时长,该如何做?在业务方法的第一行和最后一行统计时间,计算结果后打印到控制台。但这样就侵入了原始代码,并且我们是不希望与业务无关的代码出现的。为每个接口实现类创建一个代理类,将实现类组合到代理类中,代理类实现与被代理类同样的接口,每次调用被代理类的方法时,由代理类做真正的调用。但这样也不行,如果被代理类添加了新的方法,那么代理类也要去添加新的方法,两个类之间的耦合度过高,维护成本也随之升高。此时就需要用到动态代理了。什么是动态代理?动态代理指。
2023-11-08 16:51:17 43
原创 【程序设计】设计推特
首先判断用户是否在map中存在,不存在则直接返回一个空列表,随后获取用户的关注列表,将关注列表中每个用户tweet列表的头节点加入优先级队列,优先级队列会自动按照排序规则进行排序,最后返回包含10条推文id的列表即可。当调用getNewsFeed获取推文时,我们就可以先去获取用户关注的所有人,再把这些人的推文链表的头结点加入优先级队列中进行排序,最后返回包含十条推文id的列表即可。核心且最难实现的功能是getNewsFeed,返回的结果是按时间排好序的,同时用户的关注是动态变化的。
2023-11-08 10:59:19 48
原创 【数据结构】单调栈概念及运用
索引1处的柱子高度为1,左边离他最近比它高的柱子在索引1处、右边离他最近且比它高的柱子在索引3处,那么就可以计算当前柱子能接的雨水;既然是要找离当前柱子最近且比当前柱子高的柱子,那么我们就可以定义单调栈的入栈要求为:小压大,当即将入栈的柱子不符合单调栈的要求时,就可以让栈顶的柱子出栈,并更新栈顶柱子的答案。如果最后单调栈中仍留存有柱子,不用担心它会影响最终结果,因为当没有柱子让栈中的柱子出栈时,表明在栈中的这些柱子右边没有离它们最近且比他们高的柱子,它们接不了雨水,无需计算答案。
2023-11-06 22:51:54 52
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人