JDK1.6 ThreadLocal

Hibernate中典型的ThreadLocal的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static final ThreadLocal threadSession = new ThreadLocal();  

public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}

可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,实际是放到当前线程的ThreadLocalMap这个map中。这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap,而threadSession作为这个值的key,要取得这个session可以通过threadSession.get()来得到,里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。这个session相当于线程的私有变量,而不是public的。
ThreadLocal是在每个线程中有一个map,而将ThreadLocal实例作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了。 这样设计也防止内存泄漏,1个线程被销毁,这个线程内的对象都被销毁。

JDK1.6 ScheduledThreadPoolExecutor

介绍

ScheduledThreadPoolExecutor是一种类似Timer的定时器或者说是调度器。

优点

  1. 多线程的定时调度,timer是单线程的,每个timer实例只有一个工作线程。
  2. 由于继承自ThreadPoolExecutor,更具有灵活性和伸缩性。
  3. 没有timer那种线程泄露问题,timer调度的任务如果异常终止,那么整个timer都会被取消,无法执行其他任务。

JDK1.6 ReentrantReadWriteLock

介绍

ReentrantReadWriteLock支持公平/非公平锁。写锁可以降级为读锁,但读锁不能升级为写锁。

写锁(tryAcquire)

(读锁&&写锁)或者(无读锁&&无写锁),根据公平锁判断queue empty或者queue head,再竞争锁,非公平锁直接竞争锁。

读锁(tryAcquireShared)

读锁会阻塞写锁。也会根据公平锁判断queue empty或者queue head,再竞争锁,非公平锁队列头有写请求,那么当前读请求阻塞,返回false,CAS直接竞争锁。如果是queue head,返回true,通过fullTryAcquireShared()中的CAS竞争锁。

源码分析

ReentrantReadWriteLock实现ReadWriteLock接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();

/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
}

读锁和写锁2个方法。
ReentrantReadWriteLock#Sync基于AbstractQueuedSynchronizer实现。

JDK1.6 ReentrantLock

介绍

ReentrantLock和Synchronized一样都是可重入的。
ReentrantLock与Synchronized相比较而言。

优势

支持公平/非公平锁、支持可中断的锁、支持非阻塞的tryLock(可超时)、支持锁条件、可跨代码块使用(一个地方加锁,另一个地方解锁),总之比Synchronized更加灵活。

缺点

锁需要显示解锁、无法充分享用JVM内部性能提升带来的好处等等。

源码分析

ReentrantLock实现Lock接口。

Lock

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Lock {
// 1
void lock();
// 2
void lockInterruptibly() throws InterruptedException;
// 3
boolean tryLock();
// 4
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

void unlock();
Condition newCondition();
}

标注代码分析

  1. 获取锁,如果锁无法获取,当前线程被阻塞,直到锁可以获取并获取成功为止。
  2. 在当前线程没有被中断的情况下获取锁。如果锁无法获取,当前线程被阻塞,阻塞获取锁或者中断。此处有抛出InterruptedException。
  3. 尝试获取锁。
  4. 有超时时间和抛出InterruptedException。在指定时间内获取锁或者线程中断。

JDK1.6 Map#hash#key位运算

详解

HashMap#indexFor()

1
2
3
4
5
6
7
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
// 1
return h & (length-1);
}

HashMap#hash()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Applies a supplemental hash function to a given hashCode, which
* defends against poor quality hash functions. This is critical
* because HashMap uses power-of-two length hash tables, that
* otherwise encounter collisions for hashCodes that do not differ
* in lower bits. Note: Null keys always map to hash 0, thus index 0.
*/
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}

JDK1.6 LinkedBlockingQueue

介绍

LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列。队列中的元素遵循先入先出(FIFO)的规则。新元素插入到队列的尾部,从队列头部取出元素。(在并发程序中,基于链表实现的队列和基于数组实现的队列相比,往往具有更高的吞吐量,但性能稍差一些)

JDK1.6 LinkedBlockingDeque

介绍

LinkedBlockingDeque是一种基于双向链表实现的有界的(可选的,不指定默认int最大值)阻塞双端队列。
双端队列一般适用于工作密取模式,即每个消费者都拥有自己的双端队列,如果某个消费者完成了自己队列的全部任务,可以到其他消费者双端队列尾部秘密获取任务来处理。

JDK1.6 FutureTask

功能简介

  • FutureTask是一种异步任务(或异步计算),举个栗子,主线程的逻辑中需要使用某个值,但这个值需要复杂的运算得来,那么主线程可以提前建立一个异步任务来计算这个值(在其他的线程中计算),然后去做其他事情,当需要这个值的时候再通过刚才建立的异步任务来获取这个值,有点并行的意思,这样可以缩短整个主线程逻辑的执行时间。
  • FutureTask是基于AQS来构建的,使用共享模式,使用AQS的状态来表示异步任务的运行状态。

MongoDB如何选择片键

  1. shard key需要有高的散列程度(应该包含不同的值)。也就是shard key需要拥有很多不同的值。便于数据的切分和迁移。
  2. 尽量与应用程序融合。让mongos面对查询时可以直接定位到某个shard
  3. 具有随机性。这是为了不会让某段时间内的insert请求全部集中到某个单独的分片上,造成单片的写速度成为整个集群的瓶颈。用ObjectId作为shard key时会发生随机性差情况。ObjectId实际上由进程ID+TIMESTAMP + 其他因素,所以一段时间内的timestamp会相对集中。不过随机性高会有一个副作用,就是查询性比较差。可用hash key(散列值)增加随机性。
  4. 如果片键变化少,又想它作为片键,可以选择使用组合片键。
  5. 片键数量有限,这种片键称为:小基数片键。这种片键,到数据量变多的时候,会造成数据无法分隔,磁盘逐渐耗尽的情况。
  6. 片键无法修改。
  7. 组合片键:{”值映射多个数据块”,”搜索字段”}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×