2020求职笔记(一)

引子

人生已经如此地艰难,我却还要找工作,我真的好难。一个工作不到两年的渣渣要在这个时候去找工作,这是何等的无所畏惧与狂妄!年轻人,加油!

前几天投了一些简历,经历了一些笔试、面试,这里就记录一下自己求职的心路历程,并且记录一下自己在笔试、面试中碰到的题目。

题目

1、线程的六种状态?

线程的六种状态在 Thread.State 枚举中定义:

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}
  • 当线程刚创建的时候,处于 NEW 状态,此时线程还没开始执行。
  • 当线程的start()方法被调用后,线程开始执行任务,进入 RUNNABLE 状态。
  • 当线程在执行任务的过程中 遇到了 synchronized 同步块,线程就会进入 BLOCKED 状态。
  • 如果线程在执行的过程中,碰到一些特殊的事件,线程就会进入 WAITING、TIMED_WAITING 状态。比如同步块中的对象调用 wait() 方法,那当前线程就要等待该对象调用 notify()/notifyAll()方法;通过join()方法等待的线程则会等待目标线程的终止。一旦等到了期望的事件,线程就会继续执行,进入 RUNNABLE 状态。
  • 当线程执行完毕,进入 TERMINATED 状态。

在有的书籍、文档中,将这里描述为“线程的五种状态”。比如,在阿里出品的《码处高效》这本书的第七章中,就将这里描述为“线程的五种状态”,如下图:

实际上,就是将 WAITING 和 TIMED_WAITING 也划入到了 BLOCKED 状态中,TERMINATED 换成了 DEAD 状态。不管怎么分,原理都是一样的。

2、Object.wait() 和 Thread.sleep() 的区别?

相同点:两者都可以让线程等待一定时间。

不同点:

  • 无论是 wait()方法还是 notify() 方法,在调用前必须先获得目标对象的一个监视器。在wait()方法执行完后,会释放掉这个监视器。也就是说,wait()方法和notify()方法必须包含在调用对象的 synchronized 块中。
  • wait()方法可以被主动唤醒,而Thread.sleep() 只能等待时间走完。
  • wait()方法会释放掉目标对象的锁,而Thread.sleep()不会释放任何资源。

3、Redis持久化,RDB与AOF,聊一下?

RDB的工作方式:

默认情况下,Redis将数据集的快照保存在一个 .rdb 文件中。一般情况下,我们有三种方式开启RDB持久化:

  1. SAVE命令:一般不用,原因是这是一个同步操作,在写文件期间,服务端将阻塞客户端的读写请求。
  2. BGSAVE命令:客户端执行 BGSAVE命令后,服务端将会 fork() 一个子进程去进行持久化操作,父进程仍然为客户端服务。子进程持久化完毕后会自己退出。需要注意的是,fork() 子进程 这个操作本身仍然是同步的,如果Redis中的数据集太大,fork()操作可能也会耗时。
  3. 通过配置,自动持久化。我们可以通过在Redis.conf中配置,比如【SAVE 300 1】,这就表示在 300 秒内,只要有 1 次写入操作,服务端就会自动持久化一次。其工作方式和 BGSAVE 类似。

AOF的工作方式:

每次Redis执行数据集修改的命令,都会被异步追加到AOF文件的末尾。默认情况下,AOF这种持久化方式是关闭的。

AOF有三种持久化策略:

  1. always:每一次写入命令,都追加到AOF文件的末尾。
  2. everysec:每一秒将前一秒新增的数据追加到AOF文件的末尾。
  3. no:操作系统决定何时追加缓冲区的数据。

AOF日志重写:

有两种方式可以进行AOF日志重写:

  1. BGREWRITEAOF命令:当客户端执行该命令时,服务端将开启一个AOF重写进程,重写一个 包含重建当前内存中数据集所需的最短命令序列 的临时文件。
  2. 服务端自动重写。这里不多加介绍了,详情请参阅我的另一篇专门讲Redis持久化的文章。

默认情况下,只有在后台没有进程正在进行持久化操作时,AOF重写才会被触发。

AOF日志重写,RDB前导:

在重写AOF文件时,Redis能够在AOF文件中使用RDB文件作为前导,以便更快地重写和恢复。

RDB和AOF的优缺点对比:

  • RDB文件非常紧凑,而数据集相同的情况下,AOF文件往往更大,不利于网络传输;
  • 与AOF相比,在恢复一些大数据集的时候,RDB一般都会快一些;
  • 根据使用的策略不同,RDB可能会丢失一小段时间的数据,AOF保存的数据往往更加完整;
  • AOF文件更容易被人读懂,而RDB则对人工阅读不友好。

详情参考:Redis学习笔记(一):Redis持久化——RDB与AOF

4、MySQL事务隔离级别有哪些?分别解决什么问题?

参考:《MySQL事务的特性与隔离级别》

5、JavaGC机制,简单聊一下?

参考:《JVM学习笔记(二):JVM GC机制与垃圾收集器》

6、缓存穿透、缓存击穿、缓存雪崩,是怎么回事呢?如何解决这些问题?

缓存穿透:访问一个原本不存在的Key,就会穿透缓存。流量大时,数据库会挂掉。

解决方案:

  1. 布隆过滤器很好地解决了缓存穿透的问题;
  2. 使用key进入数据库查询时,即使没有查到值,也将空值写入缓存,但可以给一个比较短的过期时间;

缓存击穿:一个存在的Key,在过期的一瞬间,大量的请求过来,这些请求都会击穿到数据库,造成数据库瞬间压力大增。

解决方案:

  1. 在访问Key之前,使用 SETNX 来设置一个短期的Key来锁定当前Key的访问,访问结束再删掉该短期Key。
  2. 给获取缓存的方法加锁,这样在高并发时大大降低了吞吐量。
  3. 定时刷新缓存。

缓存雪崩:大量的Key设置了同样的过期时间,导致大量的缓存在同一时间全部过期,造成数据库压力瞬间增大,引起雪崩。

解决方案:

  1. 在给Key设置过期时间时,加上一个随机值,使Key的过期时间分布开来,不会集中在同一时刻失效。

7、synchronized 关键字你一般加在什么地方?聊一聊锁优化?

synchronized 要加在有资源竞争的地方。常见的锁优化机制有:

  • 只在有必要时加锁,减少线程锁的持有时间;例如:正则表达式的 Pattern 类的 matcher() 方法,只在表达式未编译的时候加锁。
  • 减小锁的粒度;例如:JDK7中,ConcurrentHashMap 中的分段锁。
  • 使用读写分离锁来代替独占锁。
  • 锁分离:一个典型的例子是 JDK7中的 LinkedBlockingQueue 的实现;put() 方法 和 take() 方法,一个操作的是队头,一个是队尾,两个地方使用两把锁分别锁定。
  • 锁粗化:比如下面的例子:
for(int i = 0; i < CIRCLE; i++) {
    synchronized(lock) {
        // do something
    }
}

上面例子中,每次循环都有锁的申请和释放,但其实这没有必要,更合理的方式是,只在循环外加一次锁:

synchronized(lock) {
    for(int i = 0; i < CIRCLE; i++) {
        // do something
    }
}

这样子就能显著减少不断申请、缩放锁的时间。

总结

这公司的面试,现在就记得问了这些了。面试的问题不多,大概也就面了半个小时左右。正在整理这篇文章的时候,才发现,原来自己当时回答JVM垃圾回收算法的时候,回答的不完整,只回答了对象生存判定的两个方式。嗯,温故而知新~加油!

参考文档

1、《实战·Java高并发程序设计》第二版,葛一鸣 著,第二章、第四章。

2、《码出高效》,孤尽、鸣莎 著,第七章。

3、缓存穿透、缓存击穿、缓存雪崩概念及解决方案 - 简书

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值