BoneCP主要使用了下面几种第三方包:
1、Google Guava library The Guava project contains several of Google's core libraries that we rely on in our Java-based projects: collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth.
2、SLF4J 日志管理
3、jsr166y Doug Lea写的一个并发包,代码量不多,已合并到JDK7的JUC包中。BoneCP中使用该包下的LinkedTransferQueue队列来保存Connection对象:
if (config.getMaxConnectionsPerPartition() == config.getMinConnectionsPerPartition()){
// if we have a pool that we don't want resized, make it even faster by ignoring
// the size constraints.
connectionHandles = queueLIFO ? new LIFOQueue<ConnectionHandle>() : new LinkedTransferQueue<ConnectionHandle>();
} else {
connectionHandles = queueLIFO ? new LIFOQueue<ConnectionHandle>(this.config.getMaxConnectionsPerPartition()) : new BoundedLinkedTransferQueue<ConnectionHandle>(this.config.getMaxConnectionsPerPartition());
}
这段神奇的代码还没研究明白用意,当设置每个partition中的最大连接数和最小连接数相等时,保存Connection对象的队列就使用无参构造函数,也就是队列的最大值为Integer.MAX_VALUE,如果不相等则队列的最大值为用户设置的最大值。
BoneCP只使用到jsr166y中的3个类,合成到它自己的jar里:
TransferQueue和LinkedTransferQueue
著名的Java并发编程大师Doug lea在JDK7的并发包里新增一个队列集合类LinkedTransferQueue,
TransferQueue接口继承自BlockingQueue接口,LinkedTransferQueue是TransferQueue的实现,它基于单向双端链表,是个无界链表,它的Node仅持有其下一个节点的引用,是一个典型FIFO队列,如下为保存无线的Node类实现:
static final class Node {
final boolean isData; // false if this is a request node
volatile Object item; // initially non-null if isData; CASed to match
volatile Node next;
volatile Thread waiter; // null until waiting,存放当前线程,当执行LockSupport.park(this)时,当前线程被阻塞,当执行LockSupport.unpark(node.waiter)时,该节点对应的线程将解除阻塞
// CAS methods for fields
final boolean casNext(Node cmp, Node val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
......
}
该类对链表的操作不加锁,而是采用第三方的sun.misc.Unsafe的CAS操作来保证同步:
private static final Unsafe UNSAFE = getUnsafe();
// CAS methods for fields
private boolean casTail(Node cmp, Node val) {
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
private boolean casHead(Node cmp, Node val) {
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
}
private boolean casSweepVotes(int cmp, int val) {
return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val);
}
该类中操作链表的方法都调用下面方法来实现:
public E poll() {
return xfer(null, false, NOW, 0);
}
private E xfer(E e, boolean haveData, int how, long nanos) {
if (haveData && (e == null))
throw new NullPointerException();
Node s = null; // the node to append, if needed
retry: for (;;) { // restart on append race
for (Node h = head, p = h; p != null;) { // find & match first node
boolean isData = p.isData;
Object item = p.item;
if (item != p && (item != null) == isData) { // unmatched
if (isData == haveData) // can't match
break;
if (p.casItem(item, e)) { // match
for (Node q = p; q != h;) {
Node n = q.next; // update by 2 unless singleton
if (head == h && casHead(h, n == null? q : n)) {
h.forgetNext();
break;
} // advance and retry
if ((h = head) == null ||
(q = h.next) == null || !q.isMatched())
break; // unless slack < 2
}
LockSupport.unpark(p.waiter);
return this.<E>cast(item);
}
}
Node n = p.next;
p = (p != n) ? n : (h = head); // Use head if p offlist
}
if (how != NOW) { // No matches available
if (s == null)
s = new Node(e, haveData);
Node pred = tryAppend(s, haveData);
if (pred == null)
continue retry; // lost race vs opposite mode
if (how != ASYNC)
return awaitMatch(s, pred, e, (how == TIMED), nanos);
}
return e; // not waiting
}
}
四个特殊变量NOW、ASYNC、SYNC、TIMED在poll、put等方法调用efer方法时使用:
/*
* Possible values for "how" argument in xfer method.
*/
private static final int NOW = 0; // for untimed poll, tryTransfer,用于不带超时的poll和tryTransfer方法
private static final int ASYNC = 1; // for offer, put, add,用于offer,put和add方法
private static final int SYNC = 2; // for transfer, take,用于transfer和take方法
private static final int TIMED = 3; // for timed poll, tryTransfer,用于带超时的poll和tryTransfer方法
//在队尾插入元素,由于队列是无界的所以不会阻塞
public void put(E e) {
xfer(e, true, ASYNC, 0);
}
//在队尾插入元素,永远返回true
public boolean offer(E e) {
xfer(e, true, ASYNC, 0);
return true;
}
//同上
public boolean offer(E e, long timeout, TimeUnit unit) {
xfer(e, true, ASYNC, 0);
return true;
}
//同上
public boolean add(E e) {
xfer(e, true, ASYNC, 0);
return true;
}
//若当前存在一个正在等待获取的消费者线程,即立刻移交之;否则,会插入当前元素e到队列尾部,并且等待进入阻塞状态,到有消费者线程取走该元素。
public void transfer(E e) throws InterruptedException {
if (xfer(e, true, SYNC, 0) != null) {
Thread.interrupted(); // failure possible only due to interrupt
throw new InterruptedException();
}
}
//若当前存在一个正在等待获取的消费者线程(使用take()或者poll()函数),使用该方法会即刻转移/传输对象元素e;若不存在,则返回false,并且不进入队列。这是一个不阻塞的操作。
public boolean tryTransfer(E e) {
return xfer(e, true, NOW, 0) == null;
}
//若当前存在一个正在等待获取的消费者线程,会立即传输给它;否则将插入元素e到队列尾部,并且等待被消费者线程获取消费掉,若在指定的时间内元素e无法被消费者线程获取,则返回false,同时该元素被移除。
public boolean tryTransfer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (xfer(e, true, TIMED, unit.toNanos(timeout)) == null)
return true;
if (!Thread.interrupted())
return false;
throw new InterruptedException();
}
弹出元素有下面方法:
public E take() throws InterruptedException {
E e = xfer(null, false, SYNC, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E e = xfer(null, false, TIMED, unit.toNanos(timeout));
if (e != null || !Thread.interrupted())
return e;
throw new InterruptedException();
}
public E poll() {
return xfer(null, false, NOW, 0);
}
private static final ThreadLocal<ThreadLocalRandom> localRandom =
new ThreadLocal<ThreadLocalRandom>() {
protected ThreadLocalRandom initialValue() {
return new ThreadLocalRandom();
}
};
ThreadLocalRandom() {
super();
initialized = true;
}
public static ThreadLocalRandom current() {
return localRandom.get();
}
使用ThreadLocal来保存ThreadLocalRandom对象对于线程是私有的,这样在重置种子对象时就不用加锁:
//Random中的
synchronized public void setSeed(long seed) {
seed = (seed ^ multiplier) & mask;
this.seed.set(seed);
haveNextNextGaussian = false;
}
//ThreadLocalRandom中的方法
public void setSeed(long seed) {
if (initialized)
throw new UnsupportedOperationException();
rnd = (seed ^ multiplier) & mask;
}
而且ThreadLocalRandom中还采用了追加字节码到64个字节的形式,避免线程对缓存的竞争:
// Padding to help avoid memory contention among seed updates in
// different TLRs in the common case that they are located near
// each other.
private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
为什么追加64字节能够提高并发编程的效率呢?
Fork-Join是把一个任务递归的分解成多个子任务,直到每个子问题都足够小,然后把这些问题放入队列中等待处理(fork步骤),接下来等待所有子问题的结果(join步骤),最后把多个结果合并到一起。
更多的Fork-Join学习资料:
Java 理论与实践: 应用 fork-join 框架,第 2 部分
这个是之前用JUC包实现的一个问题,使用Fork-Join模式很容易解决上JUC时的问题:
此算法的缺点有待改进的地方是结果汇总时是被动去检测,而不是某个结果计算完成后主动去汇总,既然是分段计算,如果数据量足够大时,应该采用递归去实现分段汇总会更好
注:下面代码在JDK7下运行
/**
* Huisou.com Inc.
* Copyright (c) 2011-2012 All Rights Reserved.
*/
package thread;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
/**
* @description
*
* @author chenzehe
* @email hljuczh@163.com
* @create 2013-3-19 上午10:12:31
*/
public class CalculateWithForkJoin {
public static void main(String[] args) {
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
NumbersStructure numbersStructure = new NumbersStructure(numbers, 0, numbers.length);
int threshold = 5;
int nThreads = 5;
CalculateForkJoinTask calculateForkJoinTask = new CalculateForkJoinTask(numbersStructure, threshold);
ForkJoinPool forkJoinPool = new ForkJoinPool(nThreads);
forkJoinPool.invoke(calculateForkJoinTask);
int sum = calculateForkJoinTask.sum;
System.out.println(sum);
}
}
class CalculateForkJoinTask extends RecursiveAction {
private static final long serialVersionUID = -3958126944793236011L;
private final int threshold;
private final NumbersStructure numbersStructure;
public int sum;
public CalculateForkJoinTask(NumbersStructure numbersStructure, int threshold) {
this.numbersStructure = numbersStructure;
this.threshold = threshold;
}
@Override
protected void compute() {
if (numbersStructure.size < threshold) {
sum = numbersStructure.calculateSum();
}
else {
int midpoint = numbersStructure.size / 2;
CalculateForkJoinTask left = new CalculateForkJoinTask(numbersStructure.subproblem(0, midpoint), threshold);
CalculateForkJoinTask right = new CalculateForkJoinTask(numbersStructure.subproblem(midpoint + 1, numbersStructure.size), threshold);
invokeAll(left, right);
sum = left.sum + right.sum;
}
}
}
class NumbersStructure {
private final int[] numbers;
private final int start;
private final int end;
public final int size;
public NumbersStructure(int[] numbers, int start, int end) {
this.numbers = numbers;
this.start = start;
this.end = end;
this.size = end - start;
}
/**
* 求和
*/
public int calculateSum() {
int sum = 0;
for (int i = start; i <= end && i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
/**
* 问题分解
*/
public NumbersStructure subproblem(int subStart, int subEnd) {
return new NumbersStructure(numbers, start + subStart, start + subEnd);
}
}