专家级 5. 处理器亲和性(Processor Affinity)
  这里要讲的会更靠近硬件,也是说,当软件遇上了硬件。处理器亲和性使得你能够将线程或者进程绑定到特定的CPU核上。这意味着只要是某个特定的线程,它肯定只会在某个特定的CPU核上执行。通常来讲如何绑定是由操作系统的线程调度器根据它自己的逻辑来决定的,它很可能会将我们前面提到的线程优先级也一并考虑进来。
  这么做的好处在于CPU缓存。如果某个线程只会在某个核上运行,那么它的数据恰好在缓存里的概率大大提高了。如果数据正好在CPU缓存里,那么没有必要重新再从内存里加载了。你所节省的这几毫秒时间能用在刀刃上,在这段时间里代码可以马上开始执行,也能更好地利用所分配给它的CPU时间。当然了,操作系统层面可能会存在某种优化,硬件架构当然也是个很重要的因素,但利用了处理器的亲和性至少能够减小线程切换CPU的机率。
  由于这里掺杂着多种因素,处理器亲和性到底对吞吐量有多大的影响,好还是通过测试的方式来进行证明。也许这个方法并不是总能显著地提升性能,但至少有一个好处是吞吐量会相对稳定。亲和策略可以细化到非常细的粒度上,这取决于你具体想要什么。高频交易行业便是这一策略能大显身手的场景之一。
  处理器亲和性的测试
  Java对处理器的亲和性并没有原生的支持,当然了,故事也还没有此结束。在Linux上,我们可以通过taskset命令来设置进程的亲和性。假设我们现在有一个Java进程在运行,而我们希望将它绑定到某个特定的CPU上:
  taskset -c 1 “java AboutToBePinned”
  如果是一个已经在运行了的进程:
  taskset -c 1 <PID>
  要想深入到线程级别还得再加些代码才行。所幸的是,有一个开源库能完成这样的功能:Java-Thread-Affinity。这个库是由OpenHFT的Peter Lawrey开发的,实现这一功能简单直接的方式应该是使用这个库了。我们通过一个例子来快速看下如何绑定某个线程,关于该库的更多细节请参考它在Github上的文档:
  AffinityLock al = AffinityLock.acquireLock();
  这样可以了。关于获取锁的一些更高级的选项——比如说根据不同的策略来选择CPU——在Github上都有详细的说明。