看看ThreadLocal是什么,ThreadLocalRandom呢?

这两个知识点比较类似,恰巧在面试中也被频繁问起,所以对他两做一个简单的介绍

一、ThreadLocal:

​ 指的是线程私有的变量,通过set,get,remove等方法进行操作,对应的底层操作其实是ThreadLocalMap

1.ThreadLocalMap存的是什么

ThreadLocal里的键值对,key是threadlocal的弱引用,value是我们存放的值

2.ThreadLocalMap的key为什么是弱引用

key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。
ThreadLocalMap的key是弱引用,而Value是强引用。这就导致了一个问题,ThreadLocal在没有外部对象强引用时,发生GC时弱引用Key会被回收,而Value不会回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。
我们可以发现:由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障,set,get remove后面都会调用expungeStateEntry方法,对应的value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。

3.ThreadLocalMap是怎么解决哈希冲突的

开放寻址法

4 ThreadLocalMap为什么不定义在Thread里面,而是定义在ThreadLocal里面?

因为是线程私有的变量,所以看似定义在Thread里面更加符合逻辑,但是ThreadLocalMap不是必需品,我们并不需要一创建Thread就有ThreadLocalMap,为了减少成本,所以定义在ThreadLocal

二、ThreadLocalRandom

Random计算随机数需要两步,根据老seed计算出新的seed;根据新的seed计算出随机数多线程如果同时获得同样的老seed就会产生同样的随机数,Random采用了CAS来保证只有一个线程获取seed,但是这样自旋会消耗很多性能,ThreadLocalRandom是相当于每个线程维护一个seed变量,不用竞争了在ThreadLocalRandom中并没有存放具体的种子,具体的种子存放在具体的调用线程的threadLocalRandomSeed变量中。和ThreadLocal类似,当线程调用ThreadLocalRandom中的current()方法时,ThreadLocalRandom负责初始化调用线程的threadLocalRandomSeed变量,也就是初始化种子。当调用ThreadLocalRandom的nextInt()方法时,实际是获取当前线程的threadLocalRandomSeed变量作为当前种子来计算新种子,然后将新种子更新到当前线程的threadLocalRandomSeed变量,然后根据这个新种子计算随机数。多个线程通过current获得的ThreadLocalRandom实例,其实获取的是同一个实例instance

动态规划-字符串

  1. 两字符串的最长公共字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
String str1="ABC12345E98765432kjask";
String str2="p1234e598765432ahnxvm";
int m=str1.length();
int n=str2.length();
int start=0;
int end=0;
int maxlen=0;
int[][] dp=new int[m+1][n+1];//str1的i到str2的j的最长公共字符串
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(str1.charAt(i-1)==str2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
if(dp[i][j]>maxlen){
maxlen=dp[i][j];
start=i-maxlen;
end=i;
}
}
}
}
System.out.println(str1.substring(start,end));
}
  1. 两字符串的最长公共序列长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
//最长公共子序列长度
String str1="abc98759847j374s34897o38i";
String str2="bc&&&&&&&4&&&&&o3****8))";
int m=str1.length();
int n=str2.length();
int[][] dp=new int[m+1][n+1];
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(str1.charAt(i-1)==str2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
}
}
}
System.out.println(dp[m][n]);
}
  1. 两字符串的删除操作(就是两字符串的公共序列变形)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
//最长公共子序列长度
String str1="set";
String str2="eat";
int m=str1.length();
int n=str2.length();
int[][] dp=new int[m+1][n+1];
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(str1.charAt(i-1)==str2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
}
}
}
System.out.println(m+n-2*dp[m][n]);
}
  1. 编辑距离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public int minDistance(String word1, String word2) {
int m=word1.length();
int n=word2.length();
int[][] dp=new int[m+1][n+1];
for(int i=1;i<=m;i++){
dp[i][0]=dp[i-1][0]+1;
}
for(int i=1;i<=n;i++){
dp[0][i]=dp[0][i-1]+1;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=Math.min(dp[i][j-1],Math.min(dp[i-1][j],dp[i-1][j-1]))+1;
}
}
}
return dp[m][n];
}
  1. 阿里笔试题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 求多个字符串能构成的有序的最长长度(题目描述不准确)
* 比如abc efg zzz efghi
* 答案应该是abcefghizzz 长度是11
*/
public static int music(String[] s){
Arrays.sort(s);
int count = s[0].length();
int dp[] = new int[s.length]; //dp数组为包含当前字符串的最大长度
dp[0] = s[0].length();
for (int i = 1; i < s.length; i++) {
int j = s[i].length();
char x = s[i].charAt(0);
for (int k = 0; k < i; k++) {
char y = s[k].charAt(s[k].length() - 1);
if(x >= y){ //判断是否可以连接
j = Math.max(dp[k] + s[i].length(), j); //寻找可以连接的最大长度
}
}
dp[i] = j;
count = Math.max(count,j);
}
return count;
}

Unsafe函数六道面试题

  1. Unsafe是什么?
    Java 无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM 还是开了一个后门,JDK 中有一个类 Unsafe,底层是使用C/C++写的,它提供了硬件级别的原子操作。Unsafe为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用。

    UnSafe的功能主要有:(1)实例化一个类;(2)修改私有字段的值;(3)抛出checked异常;(4)使用堆外内存;(5)CAS操作;(6)阻塞/唤醒线程;(7)内存屏障

  2. Unsafe为什么是不安全的?
    因为官方不推荐使用,因为不安全,例如你使用unsafe创建一个超级大的数组,但是这个数组jvm是不管理的,只能你自己操作,容易oom,也不利于资源的回收.

  3. Unsafe的实例怎么获取?
    a. 在jdk8和之前如果获得其单例对象是会抛出异常的,只能通过反射获取,在jdk9及以后,可以通过getUnsafe静态方法获取
    b. 我们知道 unsafe是提供给java核心内库使用的,那么我们如何获取Unsafe的实例呢?当然是反射!
    c. 代码:

     Field f = Unsafe.class.getDeclaredField("theUnsafe");
     f.setAccessible(true);
     Unsafe unsafe = (Unsafe) f.get(null);
    
  4. 讲一讲Unsafe中的CAS操作?
    a. JUC中用到了大量的CAS,他们的底层其实都是采用Unsafe的CAS操作,
    b. CAS(比较与交换,Compare and swap)是一种有名的无锁算法,因为不需要加锁,性能比加锁搞。CAS是一个CPU指令。CAS还是一个乐观锁技术
    c. CAS存在的问题:

     i. 经典的ABA问题,危害有(以栈举例),解决方案:版本号控制,有的数据结构在高位用邮戳标记;不重复使用节点引用,而是构建新的节点,
     ii. CAS常常搭配自旋一起使用,如果自选长时间不成功,循环时间长 开销大
     iii. 只能保持一个共享变量的安全操作
    
  5. Unsafe的阻塞/唤醒操作?
    a. LockSupport类中的park与unpark方法对unsafe中的park与unpark方法做了封装,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。

  6. 实例化类的六种方式?
    a. 通过构造方法new一个对象
    b. 通过Class实例一个类
    c. 通过发射实例化一个雷
    d. 通过克隆
    e. 通过反序列化
    f. 通过Unsafe实例化一个类

背包问题

今天来个简单的背包问题大汇总

1. 01背包

​ 01背包顾名思义就是对一种物品只有选或者不选两种选择
​ 对于背包体积为W,物品数量是N,每个物品的体积是w[i],每个物品的价值是price[i]的选择,求用这个背包装下的物品价值最大
​ dp[i][j]表示数量为i体积为j的时候选出的最大价值
​ 方程:dp[i][j]=Math.max(dp[i-1][j],dp[i][j-w[i]]+prices[i])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
int N=4;//物品数量
int W=5;//体积
int[] w={1,2,3,4};//每个物品体积
int[] prices={2,4,4,5};//每个物品价值
int[][] dp=new int[N+1][W+1];
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= W ; j++) {
//在j比w[i-1]小的时候,选不了物品,所以只能dp[i][j]=dp[i-1][j];
if(j>=w[i-1]){
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i-1]]+prices[i-1]);
}else{
dp[i][j]=dp[i-1][j];
}
}
}
System.out.println(dp[N][W]);
}

优化:
上面的装态方程只与i-1有关,因此可将二维状态方程压缩成一维状态方程
方程:dp[j]=Math.max(dp[j],dp[j-w[i]]+prices[i])
但是这里按照j从小打到进行计算的话,背包会被重复计算(那就是后面的完全背包问题了),所以要从大到小进行计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
int N=4;//物品数量
int W=5;//体积
int[] w={1,2,3,4};//每个物品体积
int[] prices={2,4,4,5};//每个物品价值
int[] dp=new int[W+1];
for(int i=1;i<=N;i++){
for(int j=W;j>=1;j--){
if(j>=w[i-1]){
dp[j]=Math.max(dp[j],dp[j-w[i-1]]+prices[i-1]);
}
}
}
System.out.println(dp[W]);
}

之前一直不理解为什么j从小到大就会重复,这里引用一个例子
https://blog.csdn.net/qq_33279781/article/details/52073519

假设一个物品GG价值1000,体积为2,那么假设我们按【0…..v】这个顺序遍历,那么在j=2时,dp[2] = max(dp[2], dp[0]+1000),那么dp[2] = 1000,当j=4时,dp[4]=max(dp[4], dp[2]+1000), dp[4] = 2000,这时我们再思考一下,GG将被放进背包两次

2. 完全背包

完全背包指的就是物品的次数可以被无数次选取,非常好理解,就是之前的01背包的基础上,每个背包被重复选取,也就是我们的j从小到大的一个过程,每个背包被重复选取,求出最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
int N=4;//物品数量
int W=5;//体积
int[] w={1,2,3,4};//每个物品体积
int[] prices={2,4,4,5};//每个物品价值
int[] dp=new int[W+1];
for(int i=1;i<=N;i++){
for(int j=w[i-1];j<=W;j++){
dp[j]=Math.max(dp[j],dp[j-w[i-1]]+prices[i-1]);
}
}
System.out.println(dp[W]);
}

3.多重背包

最简单的多重背包指的是背包能够被选取给定次数,同样的道理,在j从小到大的时候背包被无数次选取,选取次数不可控制,在01背包的基础上,j从大到小排列,每个背包只会被选取一次,在这个基础上,对每个j再加一重循环,使其被选取k次,就完成了多重背包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
int N=4;//物品数量
int W=5;//体积
int[] w={1,2,3,4};//每个物品体积
int[] prices={2,4,4,5};//每个物品价值
int[] nums={3,1,3,2};
int[] dp=new int[W+1];
for(int i=1;i<=N;i++){
for(int j=W;j>=1;j--){
for (int k = 0; k <=nums[i-1]; k++) {
if(j>=k*w[i-1]){
dp[j]=Math.max(dp[j],dp[j-k*w[i-1]]+k*prices[i-1]);
}
}
}
}
System.out.println(dp[W]);
}

参考:www.acwing.com

练习:leecode 322,416,518

腾讯面经

腾讯(天美)
一面:
  1. 学什么专业的
  2. 学了多久代码
  3. 对linux了解吗?如何查看进程的指令 ps
  4. 什么是僵尸进程
  5. 什么是孤儿进程
  6. linux下常用的信号,(比如ctrl+c)
  7. linux系统调用函数
  8. 进程间通信方式
  9. 共享内存是什么
  10. new一块100m的对象占用物理内存吗
  11. 操作系统除了堆,栈还包括哪些段呢? 代码段 数据段
  12. 操作系统一个栈一般多大
  13. tcp与udp的区别
  14. 流量控制解决什么问题?采用什么算法
  15. 拥塞控制解决什么问题,采用什么算法
  16. 关闭连接的四次挥手
  17. Time wait状态,为什么要2msl?
  18. 为什么time wait可以消除本次连接产生的报文?(因为2msl,msl最长报文的生命周期)
  19. 写过socket吗?
  20. 常用排序算法
  21. 讲讲快排原理,特点
  22. 红黑树了解吗?
  23. 红黑树比平衡二叉树的优点在哪里,为什么?
  24. 红黑树比AVL好在哪里
  25. 数据库常用的索引是什么?
  26. 数据库如何实现回滚到一天前?
  27. 数据库的备份怎么实现的?
  28. 什么是冷备份什么是热备份
  29. 为什么是B+树,有什么优点
  30. B+树非叶子节点存放了什么?
  31. B+树如何利用磁盘预读特性?(因为B+树存了key,key接近)
  32. mysql读的很慢的原因怎么查询(慢查询日志和expalin)
  33. hash算法了解吗?用到哪里?
  34. 二分查找有什么特点?对数组有什么要求呢?
  35. 在哪刷题,刷了多少题?
  36. 为什么要做秒杀项目?
  37. 什么叫乐观锁,什么叫悲观锁?你的mysql怎么实现?
  38. 什么时候是表锁,什么时候是行锁?
  39. 分布式锁主要用在什么地方?你的分布式锁是怎么实现的?
  40. redis锁怎么实现?
  41. 加锁失败会怎么样?(会重复加锁)?
  42. 为什么不用乐观锁,而是用redis?
  43. 分布式锁如何实现按序获取锁?
  44. zookeeper怎么实现加锁,讲讲他的触发机制?
  45. 消息队列用来干什么的?
  46. 死信队列是什么?
  47. 秒杀项目如何保证HA?
  48. redis为什么性能更高?
  49. redis其他的数据结构一般可以用于啥功能?你用过啥?
  50. zset为什么可以做排行榜,底层是什么?
  51. 那你讲讲跳表?
  52. 回到秒杀,nginx如何高可用?
  53. 采用的什么负载均衡算法?
  54. JAVA和C++的区别?


二面:
  1. 填的上海,接受深圳吗?
  2. 老家哪里?
  3. C++了解多少?
  4. 实习三个月让你看两本五百页的书可以完成吗
  5. 单向链表的环
  6. 二叉树两个节点的最近公共父节点(递归)
  7. 不用递归怎么做(迭代)
  8. 不用上面两种方法怎么做
  9. TCP三次握手状态
  10. 如果UDP模拟TCP的拥塞控制过程,怎么做(我就说实现那四个算法)
  11. 拥塞避免是什么算法?
  12. Linux操作系统查看进程的cpu?
  13. 关系型数据库的索引和原理
  14. 王者荣耀的背包设计(前提:背包用数组实现)
  15. 微信在群里发一条消息如何广播出去?怎么实现的?用的UDP还是TCP?
  16. 当前有收到其他offer吗?


阿里面经

阿里

一面:
  1. 自我介绍
  2. IOC的流程
  3. 熟悉啥数据库
  4. 设计一个树形结构的组织
  5. 写过注解没
  6. 秒杀项目的架构
  7. 项目中遇到的最大难题
  8. 类加载流程
  9. 什么时候触发full GC
  10. 空间分配担保失败
  11. hashmap的底层
  12. 堆和栈的区别
  13. Callable和Runable的区别
  14. 抽象类和接口的区别
  15. 反射的原理
二面:
  1. 自我介绍

  2. 讲讲秒杀项目的逻辑和架构?

  3. 悲观锁性能不高为什么还要用分布式锁(单纯为了对比)

  4. 什么时候是行锁,什么时候是表锁?

  5. redis和zk做分布式锁的对比

  6. 秒杀项目需要改进的地方

  7. nginx的原理(多进程单线程多路复用)

  8. redis的原理(内存,通信,单线程,多路复用,持久化,一致性哈希)

  9. zookeeper的通信原理(想了一会,RPC)

  10. 你的xfyj项目做得都是啥内容?

  11. 标志位更新为什么要分布式锁?

  12. 成员变量i,用sychronized包括的i++线程安全吗(多次提示)

  13. 如何保证i++安全

  14. 讲讲volatile的作用和原理

  15. 讲讲JUC包下的内容(计数器,循环栅栏,信号量)

  16. 讲讲ArrayBlockingQueue

  17. 讲讲JVM内存结构

  18. 讲讲一个对象在内存中的变化过程,什么时候从堆到其他区域(我说创建对象实例在堆内存,做操作在栈里面,不懂~~~~~)

  19. 讲讲垃圾回收算法

  20. 数据库的隔离级别

  21. 如何保证缓存一致性

  22. 最近在看什么书籍

  23. 高考多少分 数学多少分

三面:
  1. 自我介绍

  2. xf项目做的啥

  3. 这个项目干啥的

  4. **项目做的啥

  5. 程序员社交平台项目做的啥

  6. 秒杀项目做的啥

  7. 讲讲平衡树

    代码测评:

    1. 判断回文字符串
    2. 最长回文字符串
    3. 快排

转行之路

背景介绍

本人本硕985材料科学与工程,学了六年材料,从我本科舍友转行,到我硕士师兄转行,到认识越来越多转行的人,终于,也带动了我这颗躁动的心,明白了什么叫及时止损,最后一年醒悟,自学计算机,怒转码农。我选择的是Java开发入手学习,一是因Java的生态比较成熟,学习资料也比较多;二是因为Java的市场需求特别大,更加容易上岸一点。

在lzy,lyq,yp等同学的帮助下,学习了一年半,也算是成功上岸。

春招:

阿里,腾讯,快手,招行信用卡中心

秋招:

快手,腾讯,字节跳动,百度

转行之路

为什么要转行?
  1. 兴趣爱好

    这个理由看似空泛,但其实对我来说却真实存在,因为化学材料这种实验性学科具有太大的不确定性,需要经过大量重复试错才有可能取得小成果,甚至继续不会,大家小时候都听说过爱迪生制作电灯的故事,失败99次才成功,虽然是个故事,但也告诉了我们,这条路注定 大部分都是在试错,只有经过不断的试错,才有可能取得成功,大家都知道爱迪生,但是更多的是失败了99次 还在继续失败的科研人员没人关注。

    因此 我选择一个容易取得成就感的方向,比如写出一道算法题,开发一个系统 都容易让人取得成就

  2. 行业安全系数

    材料化学等行业,充满着许多潜在的危险,比如我本科毕设节出的二氯甲烷、三氯甲烷,以及很多实验室存在的氢氟酸等,都需要学生去操作,如果毕业后进入对应企业上班,同样需要操作这些,生命可贵,为什么同样是寒窗苦读,怎么有些专业就要用自己的身体去奉献呢?

    具体可移步知乎霍华德大佬的回答:https://www.zhihu.com/question/452810376/answer/1828501401

  3. 薪资待遇

    薪资待遇也确实是考虑的一部分,同样的大学毕业,谁想比别人薪资低一半呢?当然也有人说计算机是青春饭,材料机械越老越吃香,确实如此,请看下图


生化环材越老越吃香

​ 当然上图可能是开玩笑,但是人在这一辈子最需要用钱的时候是20-30岁的时候,这个时候不赚钱,娶不到老婆,买不起房子,等五六十岁再来赚钱有什么意义?

  1. 及时止损

    前两天看到个词,叫温水煮青蛙,回顾自己大学研究生生活,说的太对了。选志愿的时候,听说材料是21世纪有前景的专业,听说计算机饱和了,听说找工作不看专业,看个人能力,成功的被第一志愿—材料录取,大学研究生阶段步步温水煮青蛙,沉浸在自己学院出了多少个院士,发了多少篇sci,争取保研,心中不断安慰自己:”都是混不好的才劝退,我们985材料应该混的不会太差“、”读完研究生就好了“、”计算机是青春饭,35岁就失业了“,在这样一种自我安慰下带着但行好事 莫问前程的原则,直到研究生阶段,在某些朋友的劝退下幡然醒悟,也曾想过学了六年材料再转是不是很亏,但是这里我想说,及时止损,错误的方向前进就是一种进步,何必要继续错下去呢?

综上:开始转行

开始转行

在没开始面试前,我是从来不敢想自己能进入大厂的,一开始的目标只是进一个三线互联网或者硬件公司,只能说闷头只管努力,当你抬起头来的时候发现自己已经到了曾经不敢想的地方。

19年4月开始,认识到科研做不下去的时候,就准备开始转程序员。我选择了当下最热门的人工智能开始学习(再一次盲目跟风),瞎学了一个月python,深度学习,tensorflow,感觉就是闭门造车,东学一下,西学一下,完全找不到方向,而且毫无成就感,也不知道在做什么方向。直到认识了一个大佬同学,大佬告诉我,我这样学算法,明年找工作只能成为失业大军中的一员,给我讲了很多算法的现状,什么诸神黄昏,天地寂灭等。告诉我及时换方向,虽然有点不舍,但是这个时候我已经明白了及时止损的道理,于是放弃了一个月的算法学习,开始走java后端的路线(这个时候是5月中旬)。

执于表面,不究原理

Java基础,mysql基础,servlet,spring框架,mybatis框架,springmvc框架

第一阶段就冲的很快,完全不理解原理,只知道照写代码,甚至觉得短短一个月多学这么多感觉计算机也不是那么难(太年轻==)。学了点java,html,sql语法就急于找一个实习证明自己,于是上boss直聘去学校旁边的一家外包公司面试,面试啥都不会。问我JAVA的三大特性我都不知道,当时就感觉自己忽略了很多细节,在外包公司干了几天,啥都不会,代码也看不懂,再此期间小哥告知我去学习ssm。于是就一头扎进了ssm的学习,照着操作一遍还是似懂非懂,后来在b站看的黑马视频课程,总算是大概明白了ssm怎么操作和基本原理。

复盘: 无人指路,瞎猫乱撞,会白学很多无用的东西,也没有找资源的途径

讯飞实习,开始实践

当时趁着,暑假出去玩了一趟,这个时候已经8月中旬了,在boss直聘上收到来自科大讯飞的一条消息,问我愿不愿意去实习,随后就去参加了面试,这个时候已经能答出来一点基础东西了,包括synchronized的用法,事务的ACID这些,答得一般般,可能看到我学校不错,还是让我过了面试。进去之后做了一个运营管理平台,大概用到的技术栈就是ssm,加上Shiro,Redis这些,因此学习了一些权限管理框架和Redis。在讯飞做的就基本是这个事情,一些CRUD,在当时也做得挺开心。这个时间段是比较辛苦的一个时间段,因为公司离学校是12公里,所以我早上6点多起床,然后坐车一个多小时到公司上班,晚上下班七点多到学校还没吃饭,有时候晚上回来还要做点实验。不过那个时候总归是开心的,这个时间段,很幸运的认识了一个重要的同学,zy同学(目前就职于拼多多),在这之后,我终于不再是闭门造车,有人给我讲就业行情,有人给我讲大厂招聘消息,有人帮我改简历。元神给我提供了很多有用的资料,还教我如何去描述项目,并且让我少走了很多弯路 。

在此期间学习了一些RedisCluster架构,参考https://www.nowcoder.com/discuss/364714?type=post&order=time&pos=&page=0

复盘:有人带路,视野会开阔不少,了解行情和正确的学习方式很重要

自学微服务、分布式、高并发

11月底十二月初,快三个月了,我就从讯飞离职,学了点中间件,准备自己做一个项目,当时不知道做什么,我从网上找了,是一个微服务项目,用的技术栈还挺繁杂,SpringBoot,SpringCloud,RabbitMq,Redis,MongoDb、ElasticSearch等,之后我再自己加入了一些功能,包括延时队列、分布式锁、消息队列重复消费、消息丢失、幂等性等方案解决办法,这也成为了我春招能吹的一些微服务,分布式的一些东西。不过后面事实证明,这种项目漏洞还是很多,幸运的是面试官都没有深究。

复盘:SpringCloud还是太重了,个人感觉小公司用的多,可以学习Dubbo、Grpc这些,并去阅读源码或了解原理,能够更好的跟面试官交流。

巩固基础

操作系统、网络、数据结构、以及Java相关基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java基础:缓冲池,String,hashcode和equals,抽象类和接口,static,final,多态;

集合:ArrayList, LinkedList,HashMap,HashSet,ConcurrentHashMap,CopyOnWriteArrayList,Queue,PrriorityQueue

JVM:JVM内存区域,垃圾收集算法,垃圾收集器,类加载机制,JVM调优

多线程:Synchronized,volatile, CAS,ReentrantLock,Countdownlatch, Semaphore,线程池,Future等

Mysql:B+树索引,存储引擎的区别,覆盖索引,联合索引,最左匹配原则,三大范式,四大隔离级别,MVCC,锁,binglog的能日志的原理和作用,查询一条语句或更新一条语句的流程

Redis:优点,单线程,事件模型,持久化,主从复制,redis集群,缓存一致性,键删除策略,淘汰策略,分布式锁,缓存击穿,缓存雪崩,缓存无底洞,数据类型底层实现,SDS、跳表、渐进式扩容等

Zookeeper: zab协议(加分),结构模型,功能和优点,分布式锁

分布式相关:CAP,BASE,Paxos,分布式事务解决方案,ZAB,分布式锁,消息队列
面试阶段:

因为一开始就没想着去大厂实习(导师不可能同意),听说阿里可以直通车,2月底,投了阿里巴巴试试,也没打算过,就想着练习练习,结果很顺利的面的两个部门通过了,当时想多试试,又投了腾讯,结果都过了,当时是出乎我意料之外的。当时我是第一批拿到意向书的人,拿到阿里意向书的时候,那天晚上高兴的没怎么睡着。因为听说阿里流程很慢,要很久才能入职,于是投了个流程快的快手,所以上半年在快手实习,之后又去了阿里实习一阵,后续秋招开启就没什么太多惊喜的了,因为无非就是重复春招的流程,很幸运的时候,面过的公司都拿到了offer。那么在这半年面试期间,其实也是穿插有一个学习和复习的过程的,主要是针对源码和面经主要如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
需要了解的源码:

集合:ArrayList, LinkedList,HashMap,HashSet,ConcurrentHashMap,CopyOnWriteArrayList,Queue,PrriorityQueue

AQS:AQS的原理,ReentrantLock,Countdownlatch, Semaphore

线程池的源码

Spring源码:初始化流程,循环依赖

上面只是一些常问的源码,一些更细节(Rpc,Netty这些)的就不提出来了。包括春秋招期间跟同学们讨论的很多面经细节知识。

当然算法的话,应该是整个学习过程中都要刷的,一般leecode 前200+剑指就够用,但是运气不好也有几率碰到难点的算法题,比如手撕跳表,比如手撕前缀树等。

面经不附了,牛客一堆。

复盘:面试经验的积累和思考非常重要,有些知识是在面试中学到的,所以一开始就要多去面试

offer选择

offer选择需要考虑的很多,待后续贴出~

总结

以上就是我为什么转行,以及转行的经历,开发这一行不需要很强的天赋,只需要踏踏实实的学上一年就可以,正确的学习路线非常重要,欢迎大家关注本公众号,并后台回复”后端学习路线“获取学习脑图~