重拾 Java 基础

31次阅读

共计 2931 个字符,预计需要花费 8 分钟才能阅读完成。

引言
最近好久没有遇到技术瓶颈了,思考得自然少了,每天都是重复性的工作。
阿里开始招实习,同学问我要不要去申请阿里的实习,我说不去,个人对阿里的印象不好。
记得去年阿里给我发了邮件,我很认真地回复,然后他不理我了。(最起码的尊重都没有,就算我菜你起码回复我一下啊?)
这种不尊重人的公司感觉去了也不快乐,当程序员最重要的就是快乐,不快乐写什么代码?

电话面
同学很友好地分享了他的阿里电话面经验。
问的都是看功底的问题,和开发经验无关 (估计写上个几年代码不写框架应该也不知道这个)。
Java 中的 HashMap、transient、volatile、HTTP301/302、生产者消费者算法。
HashMap 都问烂了,问的是 HashMap 的底层原理,我知道你们自己写过 JDK,请不要再问我 HashMap 里的 put 操作是怎么实现的了!
问源码的真的很讨厌,有什么意义吗?看过的就能答上,没看过的就答不上。
基础学习
transient
这并不是第一次听到 transient 这个词了,之前也用过,当我们使用 YunzhiService 进行综合查询时,我们会在实体中构造不映射到数据表的属性用于查询。
对于这些不映射为数据表字段的属性,我们使用 @Transient 注解。
那 Java 中被 transient 修饰又是什么意思呢?

为什么要有 transient?StackOverflow 的解释通俗易懂。
The transient keyword in Java is used to indicate that a field should not be part of the serialization (which means saved, like to a file) process.
Java 中的 transient 关键字,意味着该字段不参与序列化,意味着不被保存,例如输出到文件中。
序列化?fastjson 应该用到了这个关键字。

厉害厉害,fastjson 开发团队基本功扎实。
volatile
这个关键字也不知道怎么能给大家通俗的讲出来,还是从实际的小例子出发吧?
大家回忆一下我们之前的单例模式,单例模式很常用,这个是必须要会的。
这是有问题的懒汉模式,多线程的时候就不能保持单例了。
public class Singleton {

private static Singleton instance;

private Singleton() {

}

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
修正之前的问题

纠正一下之前博客中的一个问题,之前这样写虽然也能实现,但是效率极低,因为每次 getInstance 的时候都会被 synchronized 阻塞。
public class Singleton {

private static Singleton instance;

private Singleton() {

}

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
为了效率,不能在方法上加锁,所以需要在新建单例的时候加锁,保证只要只有一个单例被 new 出来。
看起来是没问题的,因为我们想当然的以为,一个线程 new 出来的 Singleton,赋值给 instance,然后另一个线程获取到的 instance 就一定不是空。实际上呢?

CPU 结构
让我们来看 Youtube 上的一张图:

在 CPU 内部结构中,thread- 1 和 thread- 2 运行在不同的核心上,每个核心有一个 local cache,两个线程执行时,会将变量从 shared cache 读取到 local cache。
所以 thread- 1 把 flag 内容改变了,但是 thread- 2 获取的 flag 还是从 local cache 中获取的,所以还是 true。
直到,thread- 1 的 flag 更新到 shared cache,然后再更新到 thread- 2 的 local cache,thread- 2 才知道 flag 变了。
所以我们的单例也一样,线程 A 新建了单例,然后其他线程再获取的时候,不一定是线程 A 所创建的单例对象。
拯救世界
volatile 来拯救世界了。
private static volatile Singleton instance;
volatile 做了两件事,强制将 local cache 写入到 shared cache,同时使其他核心中的 local cache 对该数据的缓存无效。
所以,完整的单例应该是这样:
public class Singleton {

private static volatile Singleton instance;

private Singleton() {

}

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
真正的单例
上面的讲解只是为了让大家了解 Java 中 volatile 的作用,实际的单例并不这样实现,而是使用私有静态内部类实现懒汉模式,当访问 getInstance 方法时,才加载 Holder 类,实例化单例。
public class Singleton {

private static class Holder {
private static Singleton instance = new Singleton();
}

private Singleton() {

}

public static Singleton getInstance() {
return Holder.instance;
}
}
呼,长出了一口气,两个晚上了,总算把 volatile 自己学会然后讲明白了,这个应该是发生的概率很小很小,我为了让 volatile 的验证让大家都看到,使用 JDK 自带的线程池,模拟实际的多线程环境,分别执行自己的测试代码,但还是没出现问题。

HTTP 301/302
去火狐开发者文档看看:
301 Moved Permanently
永久重定向,请求的资源已经被移动到了由 Location 头部指定的 url 上,搜索引擎会根据该响应修正。
HTTP 升级到 HTTPS 时应该能用到。
302 Found
临时重定向,请求的资源被暂时的移动到了由 Location 头部指定的 url 上。浏览器会重定向到这个 url,但是搜索引擎不会对该资源的链接进行更新。
可能会在某个后台服务瘫痪的时候再转给别的后台服务器时用到?
生产者消费者
至于最后的生产者消费者算法问题,我觉得意义不大,毕竟操作系统的课本出自七位河北工业大学操作系统教师之手。

JDK 中有阻塞队列,用的就是生产者消费者模型。用到的时候再说吧。
总结
每个人都是优秀的人,每个人都值得尊敬。
软件生而自由,不受世俗污染,不受凡尘打扰,我祝愿每一位软件工程师都能生活在自由、快乐之中。

正文完
 0