- 博客(84)
- 收藏
- 关注
原创 ThreadLocal详解
但是,要求每个Thread都会退出,是一个极其苛刻的要求,对于线程池来说,大部分线程会一直存在在系统的整个生命周期内,那样的话,就会造成value对象出现泄漏的可能。这样设计的好处是,如果这个变量不再被其他对象使用时,可以自动回收这个ThreadLocal对象,避免可能的内存泄露(注意,Entry中的value,依然是强引用,如何回收,见下文分解)。虽然ThreadLocalMap中的key是弱引用,当不存在外部强引用的时候,就会自动被回收,但是Entry中的value依然是强引用。
2024-10-30 14:16:29 821
原创 强引用、弱引用、软引用、虚引用
Java中4种引用的级别和强度由高到低依次为:强引用 -> 软引用 -> 弱引用 -> 虚引用当垃圾回收器回收时,某些对象会被回收,某些不会被回收。垃圾回收器会从根对象Object来标记存活的对象,然后将某些不可达的对象和一些引用的对象进行回收。引用类型被垃圾回收时间用途生存时间强引用从来不会对象的一般状态JVM停止运行时终止软引用当内存不足时对象缓存内存不足时终止弱引用正常垃圾回收时对象缓存垃圾回收后终止虚引用正常垃圾回收时跟踪对象的垃圾回收垃圾回收后终止。
2024-10-25 21:05:45 1020
原创 Java线程池详解
Executor框架是 Java5 之后引进的框架,在 Java 5 之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免 this 逃逸问题。this 逃逸是指在构造函数返回之前其他线程就持有该对象的引用,调用尚未构造完全的对象的方法可能引发令人疑惑的错误。Executor框架不仅包括了线程池的管理,还提供了线程工厂、队列以及拒绝策略等,Executor框架让并发编程变得更加简单。Executor。
2024-10-25 20:01:39 537
原创 悲观锁实现与原理
同步器的设计是基于模板方法模式,该模式是基于继承的,主要是为了在不改变模板结构的前提下在子类中重新定义模板中的内容以实现复用代码使用者继承并重写指定的方法将 AQS 组合在自定义同步组件的实现中,并调用其模板方法,这些模板方法会调用使用者重写的方法AQS 使用了模板方法模式,自定义同步器时需要重写下面几个 AQS 提供的模板方法(钩子方法isHeldExclusively() //该线程是否正在独占资源。只有用到condition才需要去实现它tryAcquire(int) //独占方式。
2024-10-25 15:07:03 879
原创 CAS在Java中的实现
悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了(具体方法可以使用版本号机制或 CAS 算法)。
2024-10-23 21:36:06 600
原创 JMM和volatile关键字
一般来说,编程语言也可以直接复用操作系统层面的内存模型。不过,不同的操作系统内存模型不同。如果直接复用操作系统层面的内存模型,就可能会导致同样一套代码换了一个操作系统就无法执行了。Java 语言是跨平台的,它需要自己提供一套内存模型以屏蔽系统差异。对于 Java 来说,你可以把 JMM 看作是 Java 定义的并发编程相关的一组规范,
2024-10-23 19:39:20 932
原创 线程和进程概念及API
字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。需要注意的是,如果执行的是 native 方法,那么程序计数器记录的是 undefined 地址,只有执行的是 Java 代码时程序计数器记录的才是下一条指令的地址。所以,程序计数器私有主要是为了线程切换后能恢复到正确的执行位置。
2024-10-23 15:43:04 819
原创 JIT详解
在Java中,JIT(Just-In-Time)编译器是Java虚拟机(JVM)的一个重要组成部分,它负责将Java字节码转换成特定平台的机器码。Java的JIT编译器的目的是提高程序的执行效率,特别是对于长时间运行的Java应用来说,JIT编译可以显著提高性能。这也解释了我们为什么经常会说。在 JDK 的源码里,也有很多被 @ForceInline注解的方法,这些方法,会在执行的时候被强制进行内联;答案是否定的,通过逃逸分析,JVM 能够分析出一个新的对象的使用范围,从而决定是否要将这个对象分配到堆上。
2024-10-16 19:32:00 892
原创 类加载过程
类从被加载到虚拟机内存中开始到卸载出内存为止,它的整个生命周期可以简单概括为 7 个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中,验证、准备和解析这三个阶段可以统称为连接(Linking)。这 7 个阶段的顺序如下图所示:一个类的完整生命周期Class 文件需要加载到虚拟机中之后才能运行和使用,那么虚拟机是如何加载这些 Class 文件
2024-10-16 19:30:50 937
原创 类加载器详解
类加载器从 JDK 1.0 就出现了,最初只是为了满足 Java Applet(已经被淘汰) 的需要。后来,慢慢成为 Java 程序中的一个重要组成部分,赋予了 Java 类可以被动态加载到 JVM 中并执行的能力。根据官方 API 文档的介绍:类加载器是一个负责加载类的对象。是一个抽象类。给定类的二进制名称,类加载器应尝试定位或生成构成类定义的数据。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。每个 Java 类都有一个引用指向加载它的。不过,数组类不是通过。
2024-10-16 19:29:56 618
原创 类文件结构
根据 Java 虚拟机规范,Class 文件通过ClassFile定义,有点类似 C 语言的结构体。//Class 文件的标志//Class 的小版本号//Class 的大版本号//常量池的数量//常量池//Class 的访问标记//当前类//父类//接口数量//一个类可以实现多个接口//字段数量//一个类可以有多个字段//方法数量//一个类可以有个多个方法//此类的属性表中的属性数//属性表集合通过分析ClassFile。
2024-10-16 19:28:55 934
原创 JVM垃圾回收机制
无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有关。JDK1.2 之前,Java 中引用的定义很传统:如果 reference 类型的数据存储的数值代表的是另一块内存的起始地址,就称这块内存代表一个引用。JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)强引用,弱引用,软引用,虚引用它们有什么区别?你知道吗?_强引用,软引用,弱引用,虚引用-CSDN博客。
2024-10-16 19:25:52 896
原创 Java内存结构
如下图所示,不同编程语言(Java、Groovy、Kotlin、JRuby、Clojure …)通过各自的编译器编译成.class文件,并最终通过 JVM 在不同平台(Windows、Mac、Linux)上运行。运行在 Java 虚拟机之上的编程语言也就是说我们平时接触到的 HotSpot VM 仅仅是是 JVM 规范的一种实现而已。除了我们平时最常用的 HotSpot VM 外,还有 J9 VM、Zing VM、JRockit VM 等 JVM。
2024-10-16 19:22:57 1041
原创 字符串常量池
这里的value就是底层用来存储字符的char类型数组,到这里我们就可以明白了,其实StringBuilder也对null的字符串进行了特殊处理,在append的过程中如果碰到是null的字符串,那么就会以"null"的形式被添加进字符数组,这也就导致了两个为空null的字符串相加后会打印为"nullnull"。就 HotSpot VM 的实现来说,加载类时字符串字面量会进入到运行时常量池,不会进入全局的字符串常量池,即在 StringTable 中并没有相应的引用,在堆中也没有对应的对象产生。
2024-10-11 14:15:12 922
原创 类加载器解析、栈动态链接、符号引用和直接引用
例如,在字节码中,一个类、方法或字段的引用可能被表示为一个符号,例如类名、方法名或字段名。经过解析阶段后,符号引用会被替换为指向具体类、方法或字段的内存地址,JVM能够通过这些地址直接访问或调用。JVM可以在类加载时立即解析所有符号引用,也可以选择在首次使用某个符号引用时(例如,第一次调用某个方法或访问某个字段时)才进行解析。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行。在解析过程中,JVM会根据不同的符号引用类型执行不同的解析操作。
2024-10-10 19:23:21 768
原创 面试经典150题 堆
中等给定整数数组nums和整数k,请返回数组中第k个最大的元素。请注意,你需要找的是数组排序后的第k个最大的元素,而不是第k个不同的元素。你必须设计并实现时间复杂度为O(n)的算法解决此问题。
2024-09-26 19:46:46 855
原创 建堆算法实现
例2 - 完全二叉树(Complete Binary Tree)特点:最后一层可能未填满,靠左对齐。例1 - 满二叉树(Full Binary Tree)特点:每一层都是填满的。在 https://www.wolframalpha.com/ 输入。计算机科学中,堆是一种基于树的数据结构,通常用。下面看交换次数的推导:设节点高度为 3。完全二叉树可以使用数组来表示。
2024-09-25 18:47:34 853
原创 OAuth 2.0第三方授权登录
用户访问客户端(第三方应用),客户端引导(重定向)用户的代理(浏览器)去到认证服务器的授权页面,这个时候客户端会在 URI 上附上 Client ID(客户端 ID,用于唯一标识)、Rediection URI(重定向 URI)和 Response Type(响应类型)、Scope(授权作用范围)等信息。2若 Access Token 未超时,那么进行 Refresh Token 不会改变 Access Token,但超时时间会刷新,相当于续期 Access Token。
2024-09-24 17:25:35 819 1
原创 排序算法Java实现
算法最好最坏平均空间稳定思想注意事项冒泡O(n)O(n2n^2n2O(n2n^2n2O(1)Y比较最好情况需要额外判断选择O(n2n^2n2O(n2n^2n2O(n2n^2n2O(1)N比较交换次数一般少于冒泡堆O(nlognnlognnlognO(nlognnlognnlognO(nlognnlognnlognO(1)N选择堆排序的辅助性较强,理解前先理解堆的数据结构插入。
2024-09-24 12:12:30 1084
原创 Redis中的BigKey
大 key 并不是指 key 的值很大,而是 key 对应的 value 很大。String 类型的值大于 10 KB;Hash、List、Set、ZSet 类型的元素的个数超过 5000个;
2024-04-15 21:01:55 1308
原创 缓存雪崩、缓存击穿、缓存穿透
缓存空对象思路分析:当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库,我们都知道数据库能够承载的并发不如redis这么高,如果大量的请求同时过来访问这种不存在的数据,这些请求就都会访问到数据库,简单的解决方案就是哪怕这个数据在数据库中也不存在,我们也把这个数据存入到redis中去,这样,下次用户过来访问这个不存在的数据,那么在redis中也能找到这个数据就不会进入到缓存了。
2024-04-15 20:07:49 803
原创 布隆过滤器
布隆过滤器(Bloom Filter,BF)是 Bloom 于 1970 年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的 List、Map、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。
2024-04-15 20:00:19 710
原创 多级缓存实现方案
在默认情况下,当一个缓存元素过期的时候,Caffeine不会自动立即将其清理和驱逐。而是在一次读或写操作后,或者在空闲时间完成对失效数据的驱逐。Caffeine 是一个超强大的高性能本地缓存框架,除了基本的缓存功能之外,Caffeine 还提供了过期、异步加载、事件通知等功能。Caffeine既然是缓存的一种,肯定需要有缓存的清除策略,不然的话内存总会有耗尽的时候。:设置缓存为软引用或弱引用,利用GC来回收缓存数据。性能较差,不建议使用。:设置缓存的数量上限。:设置缓存的有效时间。
2024-04-15 19:46:45 707
原创 Redis哨兵
Sentinel(哨兵) 只是 Redis 的一种运行模式 ,不提供读写服务,默认运行在 26379 端口上,依赖于 Redis 工作。Redis Sentinel 的稳定版本是在 Redis 2.8 之后发布的。Redis 在 Sentinel 这种特殊的运行模式下,使用专门的命令表,也就是说普通模式运行下的 Redis 命令将无法使用。或者// 指定要监视的 master// 127.0.0.1 6379 为 master 地址。
2024-04-14 17:13:13 865
原创 Redis持久化
使用缓存的时候,我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了做数据同步(比如 Redis 集群的主从节点通过 RDB 文件同步数据)。
2024-04-14 17:12:25 650
原创 Redis主从复制
单机 Redis 存在单点风险问题,也就是说,如果唯一的一个 Redis 节点宕机的话,就会导致大量的请求直接被打到数据库,严重的情况下,数据库很可能会直接宕机。这个时候,保障 Redis 服务的高可用就成为了我们不得不面对的问题。单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。如何保证 Redis 服务高可用?最简单的一种办法就是基于 主从复制 搭建一个 Redis 集群,
2024-04-14 17:09:28 546
原创 Redis实现消息队列
消息队列(Message Queue)是一种通信方式,用于在不同应用程序或不同组件之间传递消息。它允许发送者(生产者)将消息发送到队列中,然后接收者(消费者)可以从队列中获取消息进行处理。消息队列通常用于解耦系统的不同部分,提高系统的可扩展性、可靠性和性能。消息队列的基本原理是,消息发送者将消息发送到队列中,而消息接收者则从队列中获取消息。发送者和接收者之间不直接通信,而是通过队列来传递消息。
2024-04-06 15:35:55 1116
原创 Redis实现分布式锁
可重入锁是一种特殊类型的锁,允许同一个线程在持有锁的情况下多次获取同一个锁而不会发生死锁。这种锁的实现会跟踪当前持有锁的线程,并为每个线程记录获取锁的次数。当同一个线程再次尝试获取锁时,它会增加自己的锁计数。只有当锁计数为零时,锁才会真正释放。
2024-04-01 23:15:11 775
原创 拦截器未生效的问题
将路径打印出来后才发现是因为这些路径都没写,我配置的异常处理机制自动为我重定向到/error路径,这样被拦截器截获。想试验一下下边的路径是否不被拦截器拦截(这些路径都没写)显示jwt校验说明被拦截器拦截了。记录一下自己出现的一个问题。
2024-03-31 19:19:06 257
原创 Redission快速入门
在 VoucherOrderServiceImpl。如何使用Redission的分布式锁。注入RedissonClient。
2024-03-29 23:56:31 337
原创 PageHelper的使用
这些参数可以帮助您更方便地获取分页查询的相关信息,以及处理分页查询的结果。在使用 PageHelper 进行分页查询时,您可以根据需要选择合适的参数来获取和处理分页信息。这些参数和方法可以帮助您更方便地获取分页查询的相关信息,并进行分页导航和结果展示。您可以根据需要选择合适的构造方法和使用方法,以满足不同场景下的分页需求。类是 PageHelper 提供的用于封装分页查询结果和分页信息的类。类中的参数主要用于存储分页的相关信息,以及封装分页查询的结果。引入最新的就可以,引入旧版会出现循环依赖问题。
2024-03-29 21:18:34 727
原创 代码随想录 图论
逐个遍历元素,遍历到为1的元素岛屿数量就+1,通过dfs向上下左右方向的位置dfs搜索,将该岛屿首个元素能辐射到的范围中的为1的陆地全部修改为‘0’,这样该元素在再次被访问到的时候就不会计数或者向四周访问(因为每个位置向上下左右访问,访问道德位置又向上下左右访问,这样会一直访问,导致栈溢出)。广度优先遍历,遇到‘1’的情况就将岛屿数量+1, 并广度优先遍历上下左右的元素,遇到‘1’就标记已经访问过,遇到‘0’就停止向下访问。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
2024-03-26 19:26:02 1934
原创 代码随想录 单调栈
时间复杂度为O(n)。,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。,因为我们遍历数组的时候,我们不知道之前都遍历了哪些元素,以至于遍历一个元素找不到是不是之前遍历过一个更小的,所以我们需要用一个容器(这里用单调栈)来记录我们遍历过的元素。单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。,因为单纯的说从左到右或者从前到后,不说栈头朝哪个方向的话,大家一定比较懵。
2024-03-25 20:13:14 981
原创 代码随想录 动态规划-子序列问题-回文
647. 回文子串中等提示给你一个字符串 ,请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。示例 1:输入:s = "abc"输出:3解释:三个回文子串: "a", "b", "c"示例 2:输入:s = "aaa"输出:6
2024-03-24 19:35:31 691
原创 代码随想录 动态规划-子序列问题-编辑距离
t[j - 1]),此时相当于t要删除元素,t如果把当前元素t[j - 1]删除,那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,即:dp[i][j] = dp[i][j - 1];其实这里 大家可以发现和。
2024-03-24 15:38:01 701
原创 代码随想录 动态规划-子序列问题-子序列(连续)
而是dp[6]。在回顾一下dp[i]的定义:包括下标i之前的最大连续子序列和为dp[i]。那么我们要找最大的连续子序列,就应该找每一个i为终点的连续最大子序列。所以在递推公式的时候,可以直接选出最大的dp[i]。
2024-03-23 00:26:23 1022
原创 代码随想录 动态规划-子序列问题-子序列(不连续)
其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)这么分析完之后,大家可以发现:本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!
2024-03-22 15:26:07 584
原创 代码随想录 动态规划-股票问题
简单给定一个数组prices,它的第i个元素prices[i]表示一支给定股票第i天的价格。你只能选择买入这只股票,并选择在卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回0。5在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。0。
2024-03-21 15:08:12 997
原创 代码随想录 动态规划-打家劫舍
目录198.打家劫舍 213.打家劫舍II337.打家劫舍III 198. 打家劫舍中等你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。示例 1:输入:[1,2,3,1]输出:4解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
2024-03-19 23:37:25 739
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人