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 [email protected] * @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); } }