Neo's Blog

不抽象就无法深入思考
不还原就看不到本来面目!

0%

bootstrap classloader
extension classloader
application classloader
custom classloader

双亲委派模型

当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载

参考:
https://blog.csdn.net/w1lgy/article/details/126434976

top 查看cpu使用率,重点关注:内核还是用户态、load值

cpu使用率2500%左右(40核系统,但平时才300%左右),并确认当前系统load值是否很高,如果很高(超过cpu核数),说明当前系统出现大量线程排队现象,如果load比较低但cpu很高,说明系统运行很顺畅,而是业务比较繁忙导致。

top -H pid 查看哪些线程cpu占比高

jstat -gc pid 查看java内部状态

jstack 查看调用stack

strace 查看系统调用

查看函数/接口耗时

调整jvm 内存参数,调整gc方式(cms->g1)

arthas

dashboard:当前系统的实时数据面板

thread:查看当前 JVM 的线程堆栈信息

jvm:查看当前 JVM 的信息

sc:查看JVM已加载的类信息

sm:查看已加载类的方法信息

jad:反编译指定已加载类的源码

classloader:查看classloader的继承树,urls,类加载信息,使用classloader去getResource

monitor:方法执行监控

watch:方法执行数据观测

trace:方法内部调用路径,并输出方法路径上的每个节点上耗时

stack:输出当前方法被调用的调用路径

tt:方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

reset:重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类

quit:退出当前 Arthas 客户端,其他 Arthas 客户端不受影响

shutdown:关闭 Arthas 服务端,所有 Arthas 客户端全部退出


memory 各种查看


netstat 查看timewait closewait, 查看数据包长什么样,是否符合预期等

equals VS ==

(1)对象类型不同(一个是object的成员方法,一个是操作符)

(2)比较的对象不同 一个是对象内容是否ok, 一个是引用或者地址是否ok

(3)执行速度不同

在每个类中,在重写equals方法的时侯,一定要重写hashcode方法。

根据Object规范,规范约定:

如果两个对象通过equals方法比较是相等的,那么它们的hashCode方法结果值也是相等的。

如果两个对象通过equals方法比较是不相等的,那么不要求它们的hashCode方法结果值是相等的。

当在一个应用程序执行过程中, 如果equals方法比较中没有修改任何信息,那么在同一个对象上重复调用hashCode方法时,它必须始终返回相同的值。但如果从一个应用程序到了另一个应用程序,两个应用程序汇中调用hashCode方法的返回值可以是不一致的。

https://cloud.tencent.com/developer/article/1856972

优点:

1.能够运行时动态地获取类的实例,提高灵活性

2.与动态编译结合

缺点:
1)使用反射性能较低,需要解析字节码,将内存中的对象进行解析。

解决方案:

1、通过setAccessible(true)关闭JDK的安全检查来提升反射速度;

2、多次创建一个类的实例时,有缓存会快很多

3、ReflflectASM工具类,通过字节码生成的方式加快反射速度

2)相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性)

volatile

volatile 的三点:
(1)保证可见性( 主内存 VS JAVA内存)
(2)禁止指令重排

Instance = new Singleton();可以分为以下三步:
Memory = allocate();//1.分配对象内存空间
Instance(memory)//2.初始化对象
Instance = memeory //3.设置初始化的对象指向刚分配的内存地址,此时instacne ! =null

步骤2和步骤3不存在数据依赖关系,所以这种重排序是允许的

Memory = allocate();
Instance = memeory
Instance(memory)//2.初始化对象

所以这个时候出现的问题为多个线程在这里获得单例对象,第一个访问者在instance = new Singleton();这一步骤时由于指令重排序,底层先给对象分配好了地址,此时不为空,这个时候其他线程访问,instacne不为空,但是得不到实例对象。

(3)不保证原子性

volatile的应用场景

DCL(Double Check Lock双端检索机制)

双端检索机制不一定安全,原因是有指令重排序的存在,加入volatile可以禁止指令重排。 在某一个线程执行到第一次检测时,此时instance不为null,但是insatnce的引用对象可能没有初始化完成

如何保证原子性

这种问题可以使用synchronized 或者使用原子变量 来解决。原子变量通过调用unsafe类的cas方法实现了原子操作,由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许中断,也即是说CAS是一条原子指令,不会造成所谓的数据不一致的问题。

synchronized 的锁升级过程

无锁-》偏向锁-》轻量级CAS自旋锁-》重量级锁
https://blog.csdn.net/weixin_45606067/article/details/126766885

synchronized 与 lock的区别

区别如下:

1.Lock是显示锁(手动开启和关闭锁), synchronized时隐式锁,出来作用域自动释放

2.Lock只有代码块锁,synchronized有代码块锁和方法锁

3.使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供了更多子类)

4.lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现;

5.异常是否释放锁:
synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)

6.是否响应中断
lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;

7.是否知道获取锁
Lock可以通过trylock来知道有没有获取锁,而synchronized不能;

8.Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)

9.在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

10.synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度

AQS

condtion是通过Reentlock的newCondtion方法创建出来的

rwlock也是基于AQS实现的读写锁

Semaphore 是一套单独的机制,用来实现信号量,多个线程共同访问一个资源。

spring cloud 组件

euraka

robbin

hystrix

Zuul

config

Feign

AOP 切面编程

在不侵入业务代码的情况下,实现日志打印、登录鉴权等功能。

动态代理技术,标准JDK动态代理 OR CGLIB代理

spring VS spring MVC VS spring BOOT

Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的 ioc和 aop ioc 提供了依赖注入的容器 aop ,解决了面向横切面的编程,然后在此两者的基础上实现了其他延伸产品的高级功能。Spring MVC是基于 Servlet 的一个 MVC 框架 主要解决 WEB 开发的问题,因为 Spring 的配置非常复杂,各种XML、 JavaConfig、hin处理起来比较繁琐。于是为了简化开发者的使用,从而创造性地推出了Spring boot,约定优于配置,简化了spring的配置流程。说得更简便一些:Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。大家觉得挺好用,于是按照这种模式搞了一个 MVC框架(一些用Spring 解耦的组件),用开发 web 应用( SpringMVC )。然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring Boot。

Spring MVC的功能Spring MVC提供了一种轻度耦合的方式来开发web应用。Spring MVC是Spring的一个模块,式一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。

IOC

inversion of control

DI 依赖注入

第三方容器来管理,底层实现是基于反射。

循环依赖的解决

通过三个map(一级,二级,三级)创建中的对象数组
只能解决通过set + 单例模式; 无法解决:基于构造函数的循环依赖、通过set + 多例模式

给定数字N和M,你需要从数字1到N的序列中添加+或-,使得序列的和等于M。

打印出所有满足此关系的序列,例如,给定N=4 M=6,则满足条件得序列是1-2+3+4=6(一定存在此序列)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

void dfs(int u) {
//剪枝,如果当前明确的sum已经超了,则直接return
if (exceed_already()) {
return false;
}

if (u == n) {
//check 是否满足
//cmd存了切割位置,我们按照cmd寸的切割位置,把字符串切割出来,转为整数,然后求和
return true;
}

//分割
cmd[u] = 1;
if (dfs(u + 1)) {
return true;
}

//不分割
cmd[u] = 0;
return dfs(u + 1);
}

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)

Read more »

题目描述

假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖 一次 该股票可能获得的利润是多少?

例如一只股票在某些时间节点的价格为[9, 11, 8, 5, 7, 12, 16, 14]。

如果我们能在价格为5的时候买入并在价格为16时卖出,则能收获最大的利润11。

样例
输入:[9, 11, 8, 5, 7, 12, 16, 14]

输出:11

思路

  1. 暴力做法-$O(n^2)$
  2. 调整迭代顺序(找最大值改为着最小值)本质:减元素会让之前找到的的最值失效;而加元素只需要拿新加的元素与前最值比较即可

最直观的思路-暴力做法

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
int maxDiff(vector<int>& nums) {
int min_price = INT_MAX;
int max_profit = 0;
for (int i = 0; i < nums.size(); ++i) {
for (int j = i + 1; j < nums.size(); ++j>) {
max_profit = max(max_profit, nums[j] - nums[i]);
}
}
return max_profit;
}
};

优化思路

你需要知道的两个前置技巧:

双层定值拆分原则:当涉及两层循环时,可以将外层循环变量当成定值,对循环的整体结构进行整体审视,确定是否可以优化掉一层。 把外层变量当成定植之后,观察内层循环导致在干嘛。

最值计算的累积效应:当涉及最值计算时,减元素会让之前找到的的最值失效;而加元素只需要拿新加的元素与前最值比较即可

我们的优化,需要你知晓上面的两个技巧,然后做适当的启发式思考,具体过程我已经呈现在下面的注释中了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int maxDiff(vector<int>& nums) {
int min_price = INT_MAX;
int max_profit = 0;
for (int i = 0; i < nums.size(); ++i) {
//如果把i当作定值,那么下面的这层循环是在寻找num[j]的最大值
//i每增加1,内层循环会减少一个元素,根据之前的启发式,减少元素会让之前计算的最大值失效,所以不得不重新计算最大值;
//我们尝试着调整迭代顺序。
for (int j = i + 1; j < nums.size(); ++j>) {
max_profit = max(max_profit, nums[j] - nums[i]);
}
}
return max_profit;
}
};

那么,我们更换迭代顺序之后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

class Solution {
public:
int maxDiff(vector<int>& nums) {
int min_price = INT_MAX;
int max_profit = 0;
for (int i = 0; i < nums.size(); ++i) {
//调整迭代顺序后,如果把i当作定值,那么下面的这层循环是在寻找num[j]的最小值
//i每增加1,内层循环会增加一个元素,增加一个元素后,只需比较新的元素与之前的最小值;
//便可以得到新的最小值,而不需要循环查找
//nums[i]是每次的新元素
/*
写法1
寻找哪一天买入【调整顺序】
for (int j = 0; j < i - 1; ++j>) {
max_profit = max(max_profit, nums[i] - nums[j]);
}
*/

//写法2
for (int j = 0; j < i - 1; ++j>) {
min_price = max(min_price, nums[j]);
}
max_profit = max(max_profit, nums[i] - min_price);

//写法3
min_price = min(min_price, nums[i]);
max_profit = max(max_profit, nums[i] - min_price);
}
return max_profit;
}
};