1. 第一个面试大概是年前还差半个月放假的某天晚上,一个电话面试,杭州的一个偏传统公司,应该是主要做 ERP 这类的系统,最后 HR 等年后通知就没通知了。

主要问了 java 集合,包括里面 List、Set、Map 各个区别以及相关算法、Spring 的设计模式、Spring MVC 和 Servlet 的联系区别、Spring 的相关源码,我当时只说了 Spring MVC 启动、运行源码流程,@Autowire 注入的时候的 AutowireAnnotationBeanPostProcessor 的相关逻辑,顺带讲了 Bean 的生命周期,循环注入等,还有一些常见的面试题给忘了。

阅读全文 »

1. Thread:ThreadLocalMap = 1:1

每个 Thread 内部维护了一个 ThreadLocal.ThreadLocalMap 对象

2. ThreadLocalMap:[ThreadLocal, Entry] = 1:16

每个 ThreadLocalMap 内部维护的键值对是 [ThreadLocal, Entry]。
而在底层,查找的时候是通过 ThreadLocal.threadLocalHashCode & (table.length - 1) 获取值,得到 0~15 之间的值,并获取到 Entry[i],进而获取 Entry.value。

阅读全文 »

1. Spring 的核心

1. IOC/DI(控制反转/依赖注入)

注入方式: set方法、构造器、工厂方法

2. AOP(面向切面编程)

使用的是动态代理,通过 JDK(接口实现) 或 CGLib 字节码工具包的继承来实现动态代理

2. Bean 的生命周期

1. 实例化 Bean

对于 BeanFactory 容器,是懒实例;而 ApplicationiContext 容器则是容器启动时就会实例化所有的 Bean

阅读全文 »

java7 的 ConcurrentHashMap

在构造函数就初始化了 Segment
使用 Segment 可重入分段锁 + 链表结构的 HashEntry

Segment

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
static final class Segment<K,V> extends ReentrantLock implements Serializable {

private static final long serialVersionUID = 2249069246763182397L;
// 如果是多处理器则是 64,否则是 1 次
static final int MAX_SCAN_RETRIES =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
// put 的时候,通过 scanAndLockForPut 自旋锁,for 循环 64/1 次尝试获取锁,如果一直没获取到,则 lock() 将自己挂起,然后等待 put 完之后的 unlock() 将自己唤醒
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);
for (HashEntry<K,V> e = first;;) {
if (e != null) {
K k;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
oldValue = e.value;
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
break;
}
e = e.next;
}
else {
if (node != null)
node.setNext(first);
else
node = new HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();
}
return oldValue;
}
}
public int size() {
final Segment<K,V>[] segments = this.segments;
int size;
boolean overflow;
long sum;
long last = 0L;
int retries = -1;
try {
// 获取 size 时,先进行三次无锁 CAS 的合并计算,如果前后两次相同则返回结果,如果前后两次都不一样则再对每个 Segment 加锁后获取 size 进行合并
for (;;) {
if (retries++ == RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
ensureSegment(j).lock();
}
sum = 0L;
size = 0;
overflow = false;
for (int j = 0; j < segments.length; ++j) {
Segment<K,V> seg = segmentAt(segments, j);
if (seg != null) {
sum += seg.modCount;
int c = seg.count;
if (c < 0 || (size += c) < 0)
overflow = true;
}
}
if (sum == last)
break;
last = sum;
}
} finally {
if (retries > RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
segmentAt(segments, j).unlock();
}
}
return overflow ? Integer.MAX_VALUE : size;
}
阅读全文 »

1. web.xml 的加载顺序

ServletContext -> context-param -> listener -> filter -> servlet。
前面的两个用于在 web.xml 设置变量,重要的是后面三个,即 Listener、Filter、Servlet,为 javaweb 的三大组件。

阅读全文 »

0 启动流程

  1. 解析启动参数,打印 logo。
  2. 通过工厂对配置进行判断获取相应的应用服务器(默认 undertow)。
  3. 判断是否是开发模式(默认),如果是则定期对文件进行扫描(3 * 1010)。
  4. 回调各个 listener 的 onJbootStarted() 方法。

1 如何使用 main 文件启动一个应用服务器?

阅读全文 »

除非你觉得你的时间不是很宝贵,否则不要看这篇流水账式的博文,这只是篇个人的工作的学习一个总结而已,没有包含任何的技术细节
阅读全文 »