自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(43)
  • 收藏
  • 关注

原创 对AQS的源码解析理解

我们使用常用的ReentrantLock来解析AQS是怎么工作的,仅仅是自己的一些理解,希望大家指正。首先我们进入lock()我们发现,源码中是使用了一个sync.lock()来调用的,那么sync是什么呢?我们跟踪源码发现sync是ReentrantLock中的一个属性,而Sync类就是继承了我们说的AQS,这也就说明了ReentrantLock其实就是使用AQS来做到一个对于线程的阻塞和通知唤醒的管理的。我们接着向下看。我们发现sync.lock()进来之后是一个抽象方法,这其实是一种模.

2021-10-04 15:13:58 60

原创 对于Java的强、软、弱、虚引用的理解

强引用:就是我们new出来的引用对象,只要这个引用还存在对象就不会被gc回收,内存不足会出现oom软引用:软引用平常不会被回收,但是jvm在出现oom之前会回收所有的软引用来空出空间,也就是软引用不会导致oom,因为它在oom之前一定会被回收,应用场景:可以用于高速缓存,如果内存还足够就暂时保留缓存,如果内存不足了就清理删除。弱引用:如果遇到垃圾回收就会被回收,但是由于gc线程的优先级较低,所以弱引用有时也可以存活一段较长时间,ThreadLocal就是应用了弱引用,ThreadLocalMap的key

2021-09-26 23:34:17 68

原创 关于ThreadLocal为什么采用弱引用的理解

ThreadLocal为每一个线程提供了一个私有的变量,这个现象是依托于ThreadLocalMap来实现的,map中的每一个Entry的key都是指向了一个threadlocal,这是一个弱引用,当外界对指向threadlocal的强引用回收之后,就说明这个threadlocal就没用了,但是此时还有map中的key也指向了它,若是这个key是一个强引用,那么我们就无法对threadlocal进行回收,就有可能造成一个内存泄漏的问题,所以使用了弱引用来解决这个问题,只有弱引用指向的对象,在下次垃圾回收时就

2021-09-26 21:46:11 3888 1

原创 关于hashmap头插法和尾插法的理解

头插法的初衷是设计者遵循一个新加进来的元素可能被使用的频率更高,这其实是一个伪命题,因为在hashmap扩容的时候,链表也是会发生颠倒的,因为是先从头节点开始转移掉新的hash表中。头插法还有一个致命的缺点,就是在多线程下会出现循环链的情况,导致死循环具体是怎么死循环的可以理解为因为扩容是会有一个颠倒的机制,所以多线程操作的时候有可能出现线程1让A->B 而线程2让B->A,导致了循环,jdk1.8之后改为尾插法自己的理解,希望指正...

2021-09-06 12:00:27 6345

原创 redis中zset的zrank与zscore操作时间复杂度的理解

随笔

2022-06-12 23:26:32 1903 1

原创 加权随机算法随笔

开发一个加权随机算法:核心主要有两点,一个是概率补偿,一个是即时更新权重占比 public static <T> Set<T> weightRandomSelect(Map<T, Double> weightMap, int k) { double weightSum = 0; Set<T> result = new HashSet<>(); for (Map.Entry<T, Dou

2022-04-13 00:00:23 456

原创 关于Java对象是否都在堆中分配的理解

Java在jdk1.7之后时默认开启栈上分配的,栈上分配基于逃逸分析以及标量替换来实现。逃逸分析:若是一个对象的创建和消亡都发生在一个方法内,在外界无法发现这样一个对象的存在,那么这个对象就是没有发生逃逸的。若是一个对象赋值给了类中的成员变量,或者return出去了,或者以其他的方法流出到外界了,那么就是发生了逃逸。标量替换:当jvm发现一个对象是没有发生逃逸的时候,而且就会对这个对象进行标量替换,将其拆解为标量,存储到栈中(好处,省去了对象头的消耗,节省了内存空间,也提升了应用程序性能)栈上分配的的

2022-03-03 10:08:34 324 1

原创 git使用中遇到的问题

1、开发分支使用reset之前版本之后无法push成功<-- 强制推送>git push -f origin <branch name>2、回退分支之前的版本reset,可以先使用git reflog来查到对应的commit id 之后使用git reset --hard <commit id>学习过程中...

2022-02-28 18:46:32 307

原创 HashMap的resize()方法回顾

final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap &gt.

2022-02-23 20:22:40 88

原创 对于扫描二维码登录账号的一些理解

首先二维码中的信息其实就是一个服务器的链接以及一个唯一的UUID,在扫码登陆时,首先是浏览器生成一个二维码,然后手机扫描二维码获得访问服务器的一个链接以及UUID,然后手机会访问服务器并携带UUID来证明是自己扫描了这一个二维码,服务器验证UUID之后会向手机发送确认登陆的请求,手机端点击确认登陆之后,服务器就会向对应的浏览器颁布一个令牌(因为UUID是唯一的,所以服务器是知道是那个浏览器生成的UUID的,其实也是浏览器会在生成二维码之后一直向服务器轮询是否登录),也就是完成了登录操作。自己的一些理解,

2021-12-14 14:03:06 4094

原创 对于默认的四个线程池的原理及其使用场景理解

newFixedThreadPool:是一个定长线程池,也就是核心线程数和最大线程数相等,阻塞队列采用LinkedBlockingQueue,是一个无界队列使用场景:适用于CPU密集型的任务,已有的线程数量已经可以充分的利用CPU的性能,不需要再去创建额外的线程缺点:当大量的任务提交过来时可能会造成一个任务的大量堆积,从而导致阻塞队列的元素过多造成占用一个大量的内存newCachedThreadPool:阻塞队列采用的是SynchronizedQueue,是一个容量为零的队列,也就是说每当一个生产线程

2021-12-13 22:23:49 633

原创 对于CMS产生浮动垃圾和G1的一些理解

CMS之所以产生浮动垃圾是因为在并发标记阶段,用户线程和回收线程一块工作,可能会产生之前存活的对象在这段时间消亡了,但是这个已经消亡的对象在初始标记时被标记成了黑色(三色标记法),那么后面就不会对其进行回收,只能等到下一次GC时被回收这里说一下并发标记阶段会产生的两个问题、1、漏标:漏标的意思就是存活的对象没有被标记,之所以会产生这种现象还是并发标记过程中用户现场和回收线程一块执行,可能会发生之前标记过的黑色对象突然和某个白色对象建立起了链接(可以认为是白色对象使用finalize方法复活了)此时因为黑

2021-12-12 17:03:29 2794

原创 大量ip找出出现次数最多的100个的一些理解

我们可以就是借鉴索引这一思想,在磁盘中维护一个B+树索引,然后依次统计每一个数据出现的次数,每一次在内存中遍历到一个数据时都会将他放入到索引中,若是索引中已经存在了,那么就将数量+1,最后我们再维护一个大小为100的小顶堆来找到出现最多的100个。之所以使用索引来存数据是因为我们在遍历到一个数据的时候可以利用索引的快速查找来确定这个数据是否在之前已经出现过了。...

2021-12-03 21:48:56 344

原创 算法:两数相加不使用+和-

0+0=0,0+1=1,1+0=1,1+1=0(进位)我们只需要考虑到进位的问题,相加就是等价于异或的class Solution { public int getSum(int a, int b) { while (b != 0) { // 这个就是找到a和b都是1的位置,因为相加进位到下一位,所以左移一位 int carry = (a & b) << 1; // 所有需要进位的bit

2021-12-03 17:22:48 85

原创 对于大量数据找中位数的处理方式理解

当数据量过大时我么没有办法将数据一次性加载到内存中排序,那么我们就需要分批加载假如有100G的int数据,我们内存只有2G左右,那么我们就可以分为50次加载入内存,我们将所有int类型按照区间分为10000份,大概会分为四十多万个区间,我们在内存中维护一个数组,来记录每一个区间中的数据数量,以及一个count来记录数据的总量,我们每一次加载进内存中都将这一批次的每一个数据刷入到它应该去的区间,并且将该区间的总数+1和count+1,那么遍历完数据之后我们就可以根据每一个区间的数量以及数据的总量来得到中位数

2021-11-06 12:34:00 342

原创 对于synchronized加锁原理的理解

从底层来讲的话,synchronized加锁的原理就是利用了管程来做到一个线程安全的目的,而Java中管程的实现就是Monitor对象,Monitor这一数据结构其实我认为和AQS是非常相似的,Monitor中维护了一下几个变量owner:指向持有ObjectMonitor对象的线程idWaitSet:存放处于wait状态的线程队列,即调用wait()方法的线程EntryList:存放处于等待锁block状态的线程队列count:约为WaitSet 和 EntryList 的节点数之和cxq: 多

2021-11-01 20:09:03 157

原创 对于jdk8之后使用元空间取代永久代的理解

jdk8之后使用元空间代替永久代来作为方法区的实现,那么为什么要这样呢?1、硬件的发展,电脑从32位发展到了64位,在32位电脑中内存的最大容量为2^32bit,也就是4G,此时若是将方法区放在物理内存上势必造成空间紧缺,而64位中除去16位的保留位,剩下的可支持内存仍是非常大的,此时就考虑将方法区的实现放在物理内存了2、Java的发展,比如Spring的启动就需要加载大量的类,若是还将方法区放在堆中,就会对堆的内存管理造成负担...

2021-10-30 11:53:55 213

原创 在使用回溯来求解不重复组合的一些理解

力扣40题:组合总和不重复组合的一点理解重复元素造成的重复可以使用vis数组来标记 if(i > 0 && candidates[i] == candidates[i-1] && !vis[i-1]) { continue; }先后次序造成的重复可以使用start来避免 for(int i = start; i < candidates.length; i++) {}题解:class Solution {

2021-10-26 22:11:00 98

原创 对于RabbitMQ消费者消费消息的一些理解

从队列中取到消息DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println("控制台打印接收到的消息"+message);};channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { })

2021-10-24 16:37:08 545

原创 对于排序二叉树删除节点的一些理解

1、删除叶子节点,这个没有什么特别的地方,找到目标节点然后通过父节点进行删除就可以了2、删除有一颗子树的节点,先找到目标节点target,然后找到目标节点的父节点parent,分为几种情况1)target是parent的左子树,target有左子树比如说这个,我们要删除8的话,直接让parent.left = target.left就可以了,其他三种情况,也是一样的2)target是parent的左子树,target有右子树我们删除6,就是parent.left = target.right

2021-10-10 10:54:35 372

原创 对于使用redis中的list来做到公众号推送的一些理解

我们可以假设有一个公众号是A,又111,222,333三个人订阅了它,那么当它发文章的时候我们怎么可以利用redis中的list这一数据结构来做到推送的效果呢?我们可以建立一个list,key就是Aid,value就是订阅它的读者111,222,333。每一个读者也是有一个list的,他们list的内容就是所有推送的文章id,当发布文章的时候,就可以遍历list,像每一个元素的链表中指向一个lpush的指令,将这篇文章添加到读者的list中。以上是自己的一些理解,希望大家指正。...

2021-10-06 11:26:26 178

原创 关于MySQL索引下推的一些理解

索引下推就是在查询时会在存储引擎端对where中索引列的一些条件进行判断,比如,我们对name和age两个字段建立一个联合索引select * from user where name = ‘肖%’ and age = 20;这一条语句如果没有索引下推的话就会在这两个字段的索引中找到所有肖开头的记录,然后进行回表查询,将结果返回到MySQL服务端进行一个判断age是否==20,有几个记录就需要会几次表。但是如果我们有了索引下推,那么就会在回表前利用当前索引树中的数据先进行一次判断,因为当前的索引树中是

2021-10-03 17:06:54 153

原创 对于为什么线程在io操作时需要阻塞的一些理解

为什么线程在io操作时需要阻塞呢,根本原因应该是CPU的运行速度要远超过一个io的速度,CPU不可能去等待一个要很久才能完成的io操作,所以就是当进行io操作时线程需要进入一个阻塞状态(当然,异步io的话是线程也不去等待io了,当数据准备好的时候来通知线程就好了,此处讨论阻塞io),当数据准备好了之后,才会再次进入一个CPU运行队列执行接下来的操作。为什么io的操作CPU只需要下一个指令,而不是要全程等着呢(这是我的一些理解,仅用于自己笔记,可能会有错误,也希望大家指正)我的理解是CPU就像是我们的大脑,

2021-10-01 14:55:44 2170

原创 深挖单例模式

饿汉式,DCL懒汉式,深究饿汉式public class Hungry { //饿汉式单例 //私有化构造器 private Hungry() { } //一开始就创建好实例对象,会造成资源的浪费 private static Hungry hungry = new Hungry(); public Hungry getInstance() { return hungry; }}懒汉式public class

2021-09-25 08:53:50 39

原创 关于Spring解决循环依赖的一些理解

此文仅以文字形式记录自己的一些理解Spring是采用了三级缓存的方式来解决循环依赖的问题的,一级缓存:存储的是已经初始化完成的实例对象二级缓存:实例化了但是还没有属性注入的对象三级缓存:实例化了但是还没有属性注入的对象二级和三级缓存有什么区别呢?其实若是单纯的想要解决循环依赖的问题,我们只需要二级缓存就可以解决了,具体流程如下:比如说A和B相互依赖,我们先对A初始化,在实例化之后,将A放入到三级缓存中,注入属性时发现没有B,那么去对B初始化,b实例化后注入属性去找A,在三级缓存中找到A,完成属性

2021-09-20 15:19:24 45

原创 关于AQS中的state属性的一些理解

protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSe

2021-09-16 09:53:16 1554

原创 关于ArrayList最大容量的一些理解

直接上源码源码中显示了最大容量时Integer.MAX_VALUE - 8;是不是到此就结束了,显然不是,我们来看看这句源码的注释要分配的最大数组大小。有些虚拟机在数组中保留一些头字。尝试分配较大的数组可能会导致OutOfMemoryError:请求的数组大小超过VM限制意思就是有些虚拟机时需要占用一部分空间来存对象头的(我的理解),Java为了保证不会出现OOM,所以就出现了Integer.MAX_VALUE - 8;但是我么要注意到是有些,所以就有了下面这段代码,我们首先来看grow方法,

2021-09-12 15:29:39 412

原创 关于Java泛型擦除的一些理解

泛型擦除:泛型是jdk1.5之后引入的概念,但是为什么可以和之前的版本兼容呢?就是因为采用了泛型擦除的机制,泛型信息只保留在编译阶段,在进入JVM之前,会发生泛型擦除public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); list.add(1); try { Method add = list.g

2021-09-11 22:30:33 102

原创 关于lamda表达式必须使用局部变量时需要时隐式final的一些理解

lamda表达式是会被编译器编译成一个内部类的,而内部类访问局部变量是必须要求变量不可变的,为什么呢?因为Java是可以支持一个类访问另一个类的成员变量,但是不支持一个类(包括内部类)访问另一个类的局部变量的,那么Java是怎么解决的呢,答案是编译器会将类中的局部变量拷贝出来一个副本,传入到内部类作为成员变量,因此,为了保证数据的一致性,该变量必须是不可变的。这也就解释了lamda必须使用隐式final局部变量了...

2021-09-09 21:46:38 131

原创 对于synchronized锁升级的理解

在jdk1.6之前synchronized重量级锁的观念深入人心,之前我也一直以为synchronized时重量级锁,效率很差,但是前段时间阅读了Java并发编程艺术这本书,才知道jdk1.6之后synchronized引入了锁升级的机制,synchronized不再是单纯的重量级锁了。我们可以将锁分为三类:偏向锁,轻量级锁,重量级锁此处是一些自己的理解,就不说一些难以理解的话了,若是想要深入理解原理,可以参考Java并发编程艺术这本书偏向锁:当一个线程来访问一个同步代码块时,如果时第一次有线程来访问

2021-09-09 12:16:39 92

原创 关于Java中接口能否被继承的理解

接口是Java中一种特殊的抽象类,是常量值和方法定义的集合类的继承Java中类是但继承的,但是可以实现多个接口,相信这个大家都是知道的,不允许类继承多个类的原因是,当两个父类中有同一个方法,那么子类应该继承哪个呢,但是实现多个接口就没有这一问题,因为我们是需要去实现这一方法的。但是jdk1.8之后接口引入了默认方法,那么当两个被实现的接口含有同名的默认方法呢在这里插入代码片`interface A{ default void a(){ System.out.println("

2021-09-08 16:43:42 752

原创 对于JVM虚拟机GC的理解

分代:新生代:minor GC 就是对年轻代收集,会比较频繁,比较适用复制算法老年代:major GC 一般情况下老年代GC都伴随着minor gc,但是也不是一定的(CMS的concurrent collection模式就是只收集老年代)老年代比较适用标记清除/整理算法Mixed GC:收集整个young gen以及部分old gen的GC(只有G1有这个模式)Full GCFull GC定义是相对明确的,就是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm

2021-09-06 00:20:05 107

原创 对于接口和抽象类的理解

接口时对行为的一种抽象,而抽象类是对事物的是一种抽象,抽象类是对事物的整体抽象,包括属性、行为等,而接口只是对行为的一种抽象,比如说鸟和飞机都会飞,我们就可以将飞这一行为抽象出来一个接口Fiy,里面可以有一个fly();而鸟抽象为一个类Bird,飞机为Aplane,它们两个都可以去实现Fly接口...

2021-09-05 23:23:38 50

原创 关于JVM方法区的理解

仅用于自己笔记理解《深入理解Java虚拟机 第2版》有这样一句话 --基于jdk1.7方法区(Method Area)与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。在类加载的过程中最终会生成一个java.lang.Class对象,这个对象是我们去访问方法区类型信息的接口,比如我们通过这个对象来访问类的字段、方法、运行时常量池等Class对象是存放在堆区的,不是方法区,这点很多人容易犯错。类的元数据(

2021-09-05 23:17:03 65

原创 力扣470题:使用rand7()实现rand10()的理解

仅用于自己加深记忆这一题主要记住一个公式randX()是生成[1,X]时,Y同理(randX() - 1) * Y +randY() 得到的是一个[1,X*Y]的随机函数,即randX * Y()可以论证这个是正确的数据就像是一个矩形比如X== 3,Y== 3Y 1 2 3X 0 1 2 31 4 5 62 7 8 9当我们randX==0的时候我们可以得到第一行的数据,下面同理,也就是说我们得到这些数据的概率是相等的,每一个都是1/9有了这个

2021-09-05 13:19:42 115

原创 关于同步和异步的理解

同步和异步在很多人眼里都是一种只可意会不可言传的思想在我的理解中同步就是一个线程执行一件事它会一直等到有返回结果之后再去执行下一件事异步就是一个线程执行一件事,他将任务提交之后不会等待返回值,而是直接去执行下一件事,当之前的返回值出现的时候再去通知其他的线程,可以提高效率一些自己的理解,希望指正...

2021-09-01 17:31:00 42

原创 关于静态变量的访问问题

静态变量可以通过类名点直接访问,也可以通过实例对象访问但是我们一般都建议通过类直接访问,那么是为什么呢?看一下上面一段代码,是不是觉得写的有问题,是一段一定会出现空指针异常的代码,那么结果一定是让你大吃一惊。为什么会出现这种奇怪的现象呢,一个null怎么也能访问到类变量呢?通过字节码指令7我们可以看到,其实在编译期间编译器就已经将代码优化成了类名点访问的形式了...

2021-08-31 23:26:57 1632

原创 关于包装类缓存机制的理解

Java基本类型的包装类的大部分都实现了常量池技术。Byte、Short、Integer、Long、这4种包装类默认创建了[-128,127]的相应类型的缓存数据,Character创建了[0,127]范围的缓存数据 Integer i1 = new Integer(20); Integer i2 = 20; System.out.println(i1 == i2);上面这段代码就是一个典型的例子,Integer i2 = 20;就是使用的常量池中的对象,而Int

2021-08-31 15:02:34 228

原创 关于差分数组解决区间增量的问题

差分数组解决区间增量差分数组对应的概念是前缀和数组,对于数组 [1,4,5,7],其差分数组为 [1,3,1,2],差分数组的第 i 个数即为原数组的第 i-1 个元素和第 i 个元素的差值,也就是说我们对差分数组求前缀和即可得到原数组。差分数组的性质是,当我们希望对原数组的某一个区间 [l,r] 施加一个增量num 时,差分数组 d 对应的改变是:d[l] 增加 num,d[r+1] 减少num,这样我们在使用前缀和来还原数组时区间[l,r]的增量就都是num了,需要注意的就只有r+1>len-

2021-08-31 10:01:25 149

原创 关于Java对象是否都分配在堆中的一些理解

Java对象是都分配在堆中吗?在曾经这句话是正确的,但是随着技术的不断发展,我们出现了逃逸分析这一个技术,什么是逃逸分析呢?逃逸分析是指当一个对象完全在一个方法里面生老病死,那么我们认为这个对象是没有发生逃逸的,当对象以返回值,或者被其他方法引用了,总之就是当这个对象可以出现在这个方法之外的话,我们认为这个对象是发生了逃逸的。当一个对象没有发生逃逸时,jvm就会考虑采用栈上分配这一技术,在我的理解中,栈上分配就是将这个对象由聚合量分解成多个标量(标量替换),比如Java的原始类型。然后存储在局部变量表

2021-08-28 20:03:00 163

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除