自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

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

原创 Java线程池八股文:七大参数、执行原理与避坑指南

我们假设一个点有两种表示方式,一个是普通的 xy 坐标表达式,一个是通过 半径r 和 角度a 的坐标表达式,但我们在创建Point这个类,我们发现构造方式只有一个,也就是只能通过一种方式来表达这个点,这里就可以引出我们的工厂设计模式(通过不同的静态方法实现不同的表达式创建对象)我们通常认为,可控的过程要比不可控的过程更高效,所以说如果有一段代码,需要进入到内核中,由内核负责完成一系列工作,这个过程是不可控的,我们写的代码干预不了,效率也就更低,而使用线程池,就可以省下应用程序切换到内核中运行这样的开销。

2026-06-09 13:27:29 171

原创 以“轮盘数组”思维彻底搞懂并实现阻塞队列

我们的wait等待不是一定只能有notify唤醒,还可能被Interrupt这样的方法给中断,如果使用if作为wait的判定条件,此时就存在wait被提前唤醒的风险,导致队列明明是(空/满),但我们还是执行了(put/take)方法,这会导致一些安全问题。由于接下来的put和take方法都会涉及到改,而改则会在多线程环境下产生线程安全问题,所以我们一定要记得加锁,这里我们以this作为锁。阻塞队列是一个以轮盘数组为骨架,以锁为灵魂的工具,接下来我们带着轮盘数组的思想来实现一个阻塞队列。

2026-06-08 18:48:57 210

原创 Java并发编程:彻底搞懂单例模式中的安全隐患

这时候在多线程环境下,线程A(施工队准备装修房子,也就是准备初始化完善功能),在132的情况下,线程A因为指令重排序问题,在还没装修房子的情况下就把钥匙交出去了,就在这一时刻,刚好线程B也在调用getInstance方法,成功拿到了钥匙,但一进房子,发现房子根本没装修,根本没法住!·内存可见性问题:假设这里有两个线程,同时调用getInstance方法,虽然我们说只“读”这一操作不会导致线程安全问题,但这几行代码是指“我读,读完后判断是否创建对象”,这个读就会涉及到“改”这一操作了。

2026-05-30 11:08:58 251

原创 深入理解Java并发:线程安全问题的根源与解决方案

每个线程都有一个自己的“工作内存(其实指的是CPU的寄存器)”,同时这些线程共享同一个主内存,当一个线程循环进行上述读取操作的时候,就会被主内存中的数据拷贝到该线程的“工作内存”(寄存器)上,后续别的线程修改,也是修改自己的工作内存,拷贝到主内存中,由于第一个线程任然在读自己的工作内存,因此感知不到主内存的变化。在t2线程完整完成两次++操作后,内存中count此时为2,但t1线程一开始读的count为0,此时t1先add后save,使得内存中的新count变成了从0++得到 1 ❌️❌️❌️。

2026-05-24 22:19:10 398

原创 Java线程的六大状态

我们设计start后,先让主线程睡一秒,再观察t1线程的状态,如果不睡这一秒,主线程会在t1.start之后迅速观察并打印t1线程的状态,t1线程此时的状态大概率还未第一次执行到sleep,所以我们很可能会观察到RUNNABLE,所以我们设计先睡上这一秒以便我们观察。大多数人都能脱口而出‘新建,运行,阻塞,死亡’,但这其实是操作系统的线程状态,而不是Java语言层面的答案,在Java对于线程引用提供的方法 getState( ) 的枚举中,线程其实还有更细节的六大状态。

2026-05-23 13:57:45 350

原创 Java多线程入门:掌握Thread的五大基本用法

学习线程,我们要先了解Thread类,当我们创建出Thread类的对象时,就相当于创建了一个实实在在的线程,我们通过一定手段重写其中的run()方法,也就是线程接下来要做什么,最后通过对象引用start()方法启动这个线程,当我们启动线程时,就会自动调用线程中的run()方法,这样我们的线程就跑起来了。日常开发中,我们其实很少用到Thread类中的静态方法sleep( ),更多的使用wait方法,我们使用sleep方法,并指定多少毫秒,当线程执行到这时,就会进入“睡眠”,也就是一种阻塞状态。

2026-05-21 17:29:36 210

原创 进程和线程的基本区别和联系

在90年代的时候,流行的是并发编程,也就是多进程,当时的服务器是什么情况呢:客户向服务器发送一次请求,服务器就创建并提供一个进程供客户端服务,但进程整体是一个很重的概念,创建和销毁进程的开销还是很大的,尤其是多个客户端频繁请求,就对应着频繁的创建->销毁,由此,我们就引入了线程(Thread)这一概念。当然,进程因为存在优先级,所以和其他进程并非持平等关系,并非人人都有机会,随时可以得到cpu资源,线程也一样,只能说cpu是个大舞台,我们的进程线程都是演员,每个演员都有上台表演的机会。

2026-05-20 21:06:20 39

原创 从PCB到资源调度,理解操作系统视角中的进程

一个进程执行了一会,失去cpu,过了一段时间之后,进程再回到cpu上执行,肯定是要沿着上次执行到的位置和状态继续往下执行,而不是重开,这是进程在cpu上运行时,cpu把cpu上的寄存器的值保存到了内存中(PCB对应的属性中,所以PCB做到了实时更新这个进程的各种原始记录和状态)一般我们在打游戏时,关于游戏的进程肯定要分配的cpu资源更多,所以说很多进程在cpu上运行的机会其实不是均等的,也就有优先级这一概念,进程的优先级越高,意味着该进程能吃到更多的cpu资源。

2026-05-20 20:39:49 170

原创 Java-非递归实现前中后序遍历

联想一下,我们在递归实现中序遍历时,路上碰到某个节点,要先检查这个节点的左是否为空,如果为空才能打印这个节点的值,如果不为空,则继续向left走,当无法向left走时,说明这个节点已经没有左孩子了,此时该节点符合我们中序遍历的打印条件,当无法向left走时,此时转而去检查遍历该节点的右子树,依旧同样的规则。再次联想递归实现时的思路,我们在一路往left的时候,碰到节点,需要检查该节点是否左右孩子都被打印过了,或者都为null,我们才能打印该节点的值。三、postOrder。一、preOrder。

2026-05-15 10:05:57 250

原创 Java-二叉树的底层实现2

那么如果我更改一下,在queue接收弹出并打印的cur的左右孩子时,不问其是否为null,直接offer进来,如果该树为完全二叉树,那么当我们碰到null的时候,那么就代表我们来到了最后一层的最后一个节点,其后面不可能再有节点了,并且全部为null。在写这个方法时,我们可以巧妙借助上面写的sameTree,来帮我们快速判断root这棵大树的某个分支树是否和小树subRot完全相同,如果相同,则说明其为大树的一颗子树,当然也不排除两树完全相同的情况,也就是一样大。//10.判断该二叉树是否为完全二叉树。

2026-05-14 20:06:10 374

原创 Java-二叉树的底层实现1

思路:层序遍历是一种自上而下,从左到右的打印,所以我们需要借助队列的先进先出的特性,先offer一个根节点,然后将这个根节点cur,poll吐出来并打印的同时检查其是否有左右孩子,如果有,则queue继续offer,cur的左右孩子进来,不断循环,直到队列为空。·简单来说,二叉树就是带两个分支的链表,每个节点里存着一个数据,而每个节点又有两个用引用串联起来的子节点(左孩子和右孩子),孩子里面也同样存着数据,而孩子又有孩子....思路:通过不断递归,直到走到叶子节点的孩子(空气,也就是null),返回0。

2026-05-13 20:06:57 405

原创 Java-双向链表的底层实现

3.处理措施:我们先正常 head = head.next,此时 head = null,而单向链表中head = last,但是我们要指出来,last = null,这样我们就成功处理好了单节点的情况。2.头节点不为空,我们让node节点的next指向原来的头节点head,让原来head的prev里面的null指向这个新的头节点node,最后定义head = node(让node成为新的头节点)//cur没有变,还是这个要删除的节点,不过此时已经删了,但我们还可以查看,cur的下一个节点是否为null。

2026-05-10 17:38:14 374

原创 Java数据结构-堆

我们这里选择最后一个父节点,它的孩子在usedSize-1下标的位置,我们通过(usedSize-1-1)/2求得这棵树的下标位置,从这里开始,调整完这棵树后,--,--意思就是调整这棵树的前一棵树。我们从孩子开始,先求得父节点的坐标 parent = (Child-1)/2,然后检查孩子的值是否比父亲大,大则互换,然后让Child = parent,parent = (Child-1)/2,不断向上,看看自己的父亲是否比自己大。要认识到,我们的堆就和二叉树的层序遍历一样,从上到下的打印和下标备注。

2026-03-23 16:41:11 163

原创 Java-循环队列(圆盘数组)

由此,就有了我们的圆盘数组,也叫环形数组,其利用巧妙的闭环结构解决了资源浪费这一痛点,接下来,我们来利用数组实现一个环形数组结构。所以我们将front==rear当做空的条件,我们将 (rear+1)%(数组长度)== front当做判断满了的重要条件,但是这样我们就需要浪费一个内存,去存rear,毕竟我们在数组添加完,肯定要给rear留一个空的位置,这样我们如果在构造方法中填入k,其实我们最多只用到了k-1个地方,浪费了一个内存,那我们直接写k+1不就好了吗,别的方法直接写k即可。

2026-03-16 18:55:30 240

原创 Java-链表数据结构的实现

contain这个数,同样return走人, 接下来,明确我们的思路,首先我们要获得删除目标节点的前一个节点的地址cur,然后标记一下删除目标节点的后一个节点curN,然后使得cur的next = curN即可。但这样做会有一个问题,我们在删除节点时,都是通过获得删除节点的前一个节点的地址作为辅助,那如果我们要删除头节点,头节点还有前一个节点吗?尾插法思路:同样,创建一个对象,如果head==null,说明这个链表是空的,我们就让对象head==node,然后return。//先要获得插入位置前一个节点。

2026-03-15 10:13:19 356

原创 Java-简单的洗牌抽牌小游戏

对于洗牌的思路,其实也很简单,我们让for循环中的i从cardList.size()-1开始,也就是从51开始,取一个 [0,51) 的随机数,此时i无法取到51位,就访问不到第52张牌,我们让第52张牌与随机数n号位的牌进行交换即可,循环一次后,i--,变为50,此时i无法取到第50位,也就是第51张牌,我们让第51张牌与 [0,50)的随机数n号位的牌进行交换,以此类推。在创建play方法时,我们未来肯定要让这个方法返回一个引用,并打印这个引用,但具体情况我们要返回一个什么类型的引用,我们并不清楚。

2026-03-09 17:24:58 322

原创 Java-泛型

此时,我们的myArray2这个引用指向的Object类型的数组就只能存int类型的整数了,E是我随便取的名字,一般用来表示Element,相当于帮Integer占了个位置,虽然数组类型还是Object类型,但此时我用起来的感觉就是Integer类型,这样我们就可以创建多个对象,每个引用指向的数组存放不同类型的数据,做到分工明确,而不是一股脑一个数组里放各种类型的数据,这就是泛型的好处。这样我们就规定MyArray3类型的引用存放的对象类型一定要是Number类型或是其子类类型的数据。

2026-02-23 13:38:01 573

原创 Java-异常

throws+异常 = 声明这个方法可能会出现的异常,我们通常会在方法的参数列表后throws一些异常,以用来告诉调用这个方法的人:我这个方法可能会出现某些异常,或是继续在调用了这个可能有异常的方法的参数列表后再声明可能出现的异常,交给下一个人解决。接下来我们来思考怎么创造异常,怎么做呢,我们随便写出一个异常,然后按住Ctrl 并点击这个异常名,看看这个异常是怎么写的,我们发现,很多异常都有父类吗,然后通过动态绑定+super来触发自己类型名的异常,那么我们是不是可以照着写一个看看呢?

2026-02-12 22:04:39 542

原创 Java初级图书管理系统2

(3.)满足上述条件,开始添加书籍,调用Book的构造方法,用其接收我们输入的新书的各个属性,然后通过 setbook(位置,新书)来添加图书,通过 setuseSize (新的实际书量)来更新实际书的数量。(3.)若书架上有书且书架上确有此书,则检查该书状态,若已被借出,则无法借出,若未被借出,则改变此书状态为true,return;思路:既然是还书,那我们就不考虑书架满了的情况了,直接通过for循环查找此书是否被借出过,如果被借出过,归还的时候,要记得更新一下此书的状态哦。

2026-02-10 23:39:01 517

原创 Java初级图书管理系统1

现在我们来到父类User里,写下menu方法,但发现我们访问的要么是管理员的menu菜单,要么就是普通用户的menu菜单,所以父类User的menu菜单内容根本就不重要,因为后面我们通过子类引用这个父类里的menu方法也会因为发生动态绑多访问到子类的menu方法。为什么说可能用到呢,比如我们在增加图书的时候,第一个就要判断这个书架是不是满了,所以我们要获得这个 Book[ ]类型的引用books里的地址,然后访问长度,或者没满,添加书后,我们是不是要将实际书量+1?看,我们这里就用到了继承。

2026-02-10 19:57:03 358

原创 JavaSE--String

虽然逻辑上来说,我们可以认为字符串本质上是一个char类型数组,我们可以把它当做一个数组,当然这是逻辑上,物理上,在JDK 9 之前,底层的这个 value还是 char[ ]类型的,在JDK 9 之后成了现在的byte[ ]类型,因为这涉及内存浪费问题,我们不深究,重点讲String的各种方法以及String的不可变性。我们发现上下同为a的ASCII码值,则上下都来到自己的下一位,发现上下同为e,则继续下一位,发现f的ASCII码值大于c的,则s1更大,打印出一个正数。//我们成功将所有的a替换成了k。

2026-02-10 02:09:18 374

原创 JavaSE--四种内部类

我们发现,静态内部类中无法访问data1和data2,但可以访问静态变量data3,由此我们引出静态内部类的第一个概念:静态内部类当中不能直接访问外部类的非静态成员,所以我们的data3是可以访问的,那如果我们一定要访问这些外部类当中的非静态成员呢?注意:只能这么调用内部类中的方法,也就是在方法内,内部类外,实例化一个对象,并调用类中方法,局部内部类很少用,也没什么人会往方法里写类,所以不做过多讨论。当实例内部类和外部类中同时出现了重复变量时,调用实例内部类中的方法访问这个重复变量时,会优先访问内部类的。

2026-02-01 16:07:05 366

原创 JavaSE--接口

此时我们的父类Animal中有年龄,名字等成员变量,还有eat方法,那么跑,游泳,飞这些操作能写进父类Animal中吗?答案是不行,父类负责收集的动物们的共性,而有的动物会飞,有的会游泳,有的会跑,有的都会,有的会一部分,这都是要进一步划分的,难道我们要再创造一个父类Running来划分出会跑的吗?接口就像我们日常生活中,充电的插口,有两孔的,有三角形三孔的插座,插口定义了一个统一的规范,不同的插头,只要符合这个规范,就可以和插座对接,实现通电这一功能,而插座就是接口,所以我们发现,接口类似一种“标准”。

2026-02-01 14:51:08 330

原创 JavaSE-抽象类

首先记住,抽象类的存在就是为了被其他类继承,抽象方法的存在就是为了被重写(子类继承不全部重写完就报错,除非这个子类也成为抽象类,那么抽象父类里的抽象方法不再强制要求全部重写),所以千万不要拿 final 去修饰抽象类和抽象方法,final会使得这个类不再能被继承,方法不再能被重写,这恰恰违背了其抽象类和抽象方法存在的意义,所以不能拿final去修饰。我们在抽象类中将draw方法也变成抽象方法,这个方法就可以没有具体实现了,不过记住!我们在普通的类上加入一个abstract(抽象的),这个类就变成了抽象类。

2026-01-30 15:10:28 248

原创 Java--向上转型和向下转型、instanceof

此时dog传过去之后完成了一次隐式的向上转型,注意,此时这个dog比较特殊,dog这个引用依然能调用子类的方法,父类的方法同样可以调用,正常的引用向上转型之后,不能再调用子类自己的方法,这也是向上转型的缺点。返回值的类型被声明为Animal(父类),实际返回的是Dog类型的对象(子类),返回式,Dog对象同样会被隐式转换为父类类型Animal的引用。无论是一行解决还是拆成两行来写都是同样的效果,通过这一操作,我们成功将子类对象Dog赋值给了父类类型的引用animal1和animal2。

2026-01-28 22:48:57 380

原创 Java--多态1

假如你是一个正在排队吃饭的小伙,这个时候来了一个莫名其妙的男的插了你的队,饿坏了的你雷霆暴怒,抓着他就往外拽,但这个时候来了一个165JK学姐插了你的队,你直接就把自己的位置让给了学姐,自己也不顾吃饭了,最后你跟学姐加上了微信,算了不幻想了。要想认识多态,就必须先认识什么是重写,重写就是在继承体系下,父类和子类有一个参数类型一样,方法名一样,并且子类重写的这个方法访问权限不能比父类更严格的情况下,构成了重写。System.out.println(this.name+"正在吃狗粮");

2026-01-27 20:14:18 228

原创 Java--继承2

这是我学到的第四个修饰符,他的权限没有public大,又没有default小,为什么要把他放在学到继承这个知识再说呢,因为protected的访问权限跟继承有关系,被protected修饰的东西可以在同一包的同类或不同类被访问,但在跨包访问的话就和继承有关,因为被protected修饰的东西可以在不同包中的子类被访问。Java中不支持一个类多继承,也就是一个子类无法继承多个父类,除非创造接口,不过这要后面再说了,我还没学到。不过工作我们大部分用的还是a part of,也就是谁是谁的一部分。

2026-01-27 19:45:44 417

原创 Java--继承1

1.在继承中,有两种类,父类(基类/超类)和子类(派生类),我们可以提取出这一堆子类中的共同点,放到父类里,保留子类的特殊独有代码,等子类继承父类里的东西后,就相当于子类里有了父类里的代码。可以看到父类和子类都有test1方法,那么跟变量一样,main方法中若访问test1方法,则优先访问子类的test1,同理,如果我们想要访问父类中的test1方法,可以用super.test1。前面说过super和this其实很像,所以用法也是一样的,super + "."后加变量或方法,当然,加的要是非静态的。

2026-01-25 21:08:11 258

原创 Java类与对象--代码块2

3.最后才执行构造方法的内容(所以如果构造方法里有对变量做修改,最后的变量的值就是最后执行的那个,也就是构造方法说了算)(跟实例代码块一样,根据对象数量来决定执行次数)//构造方法会在这一行被执行,但还是没有静态代码块,实例代码块执行快。1中说完了三个代码块各自的特效,接下来我想说说,当实例代码块,静态代码块和构造方法结合到一起。System.out.println("实例代码块被执行");System.out.println("静态代码块被执行");

2026-01-24 19:47:01 415

原创 Java类与对象--代码块1

2.当然,就地初始化和实例代码块在对变量的值修改过程中是讲顺序的,不过别忘了,代码打印结果不仅有a的值,还有一串实例代码块被执行的字符,注意:这串中文被先打印,a的值后打印。看上面的代码,先就地初始化classname为2班,后静态代码块里修改classname为20班,打印结果为 静态代码块被执行 和classname最后的值:20班。1.一个写在类的内部,方法外的代码块,长得和普通代码块一样,都是由不加任何修饰的花括号包起来的,一般用于定义非静态变量,可修改就地初始化过的变量的值。

2026-01-24 19:29:59 331

原创 Java类和对象3

可以看到,我们无法访问到10岁的小美的家庭住址,因为家庭住址这个变量被private这个访问修饰符所修饰,而私有变量无法在其他包其他类访问,只能在同一包同 一类中进行访问,所以我们要创造setter和getter方法来间接修改,访问这个变量,当然IDEA也有快捷创造(选择要导入的地方,右键选择Generate,选择Getter and Setter,选择要创造的对应的私有变量)静态的成员变量,也被叫做类变量,也就是说,这个变量是属于类的,不是属于对象的东西,所以我们更推荐通过类名来访问静态变量。

2026-01-23 13:37:50 272

原创 Java类和对象2

打印的结果为null今天0岁了,为什么呢,因为此时age和name作为局部变量,而局部变量优先,所以赋值操作只在内部操作,说人话就是自己给自己赋值,所以会出现null,0这些值,所以我们需要用this.来明确指向类的成员变量。这样才能正确传参并赋值,当然IDEA当然有更方便的操作,在类中,选择要导入的地方,右键选择Generate后选择Constructor后进入页面选择想要定义的成员变量(按住Ctrl多选)后生成代码。System.out.println(name+"今天"+age+"岁了");

2026-01-22 18:22:57 253

原创 Java类和对象1

Java作为一个完全面向对象的语言,封装好的方法太多了,非常好用,所以我们只要关心对象与对象之间的协作就好了,其他的不要太关心,举个例子:洗衣机,洗衣粉,衣服,人,在我们的生活中,人需要将衣服和洗衣粉都放入洗衣机后启动即可,具体洗衣机是怎么启动,注水,甩干的,我们人并不关心,所以说我们只要关心对象与对象之间的协作就差不多了。Java当中,一切皆对象,比如说一个真正的人站在你面前,你把他画出来,你的画就叫做类,而这个真正,具体的人就叫做对象,总结来看:类是描述一个对象的,对象是真实存在的实体。

2026-01-22 16:40:58 142

空空如也

空空如也

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

TA关注的人

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