Java

搜索技巧

末尾加上PDF,可以搜出一些高质量的文章,包括一些技术分享、大学的课件。

Wikipedia看参考文献,会有很多不错的资料。

关键字:Overview,Demystify

学习过程:从宏观上了解

基础

String

String.intern()

如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回。常量池是堆中的一块特殊区域。

Read More

HashMap

存储结构

HashMap内部采用数组+链表+红黑树来实现。如下图所示。

hashMap

Node是HashMap的内部类,用来表示一个键值对,实现了Map.Entry接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //用来定位数组索引位置
final K key;
V value;
Node<K,V> next; //链表的下一个node

Node(int hash, K key, V value, Node<K,V> next) { ... }
public final K getKey(){ ... }
public final V getValue() { ... }
public final String toString() { ... }
public final int hashCode() { ... }
public final V setValue(V newValue) { ... }
public final boolean equals(Object o) { ... }
}

几个重要字段

Read More

CountDownLatch&CyclicBarrier

CountDownLatch的作用是使得一个或多个线程等待直到其他线程完成操作。

CyclicBarrier的作用是可以让一组线程达到一个屏障时被阻塞,直到最后一个线程达到屏障时,所有被阻塞的线程才能继续执行。

实际场景:多线程去后台查数据,需要所有数据全部获取到后再处理。

CountDownLatch原理

还是利用AQS来实现,其对外暴露的三个重要方法为。

Read More

ReentrantLock

ReentrantLock实现了Lock的接口。ReentrantLock相比synchronized提供了更多的功能。Lock接口的方法如下

  • lock
  • lockInterruptibly,可中断的锁获取操作,在获取锁的过程中,线程可以响应中断。被中断时会抛出InterruptedException。
  • tryLock,非阻塞的获取锁,获取不到就立即返回。
Read More

ReentrantReadWriteLock

ReentrantReadWriteLock是读写锁,其维护了一对锁,一个读锁和一个写锁,读写锁在同一时刻可以允许多个读线程访问,在写线程访问时,则所有线程均被阻塞。适用于读多写少的场景。

如何在一个int变量上维护多个读线程和一个写线程的状态。读写锁将int变量分成了两个部分,高16位表示读,低16位表示写。通过位运算来实现状态的改变。

写锁

写锁是独占锁,获取锁时重写tryAcquire即可

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
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
//获取代表写锁的值,采用位运算来实现
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
// 存在读锁或者当前线程不是已经获取写锁的线程
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
// 在c=0时即没有读锁和写锁时,非公平锁会交给AQS去创建头节点然后获取再次调用获取锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}

在读锁存在的时候,写锁不能被获取,原因在于:读写锁要保证写锁的操作要对读锁可见,如果存在读锁时写锁获取到,写线程的操作就对读线程不可见。而写锁一旦被获取,则其他读写线程的后续访问就会被阻塞。

Read More