
Java多线程与CPU亲和性简介
在现代计算机系统中,多核CPU已成为标配,而JA中为了充分利用这些资源引入了多线程技术。不同的线程可以在不同的CPU或CPU核上同时运行。对于JA程序员来说,他们可以控制创建多少线程,但对于线程具体运行在哪个CPU上,却是一个“黑盒子”的难题。不过对于性能敏感的开发者来说,如果希望特定线程运行在特定CPU上,以提高性能或避免CPU切换带来的损耗,那么就需要引入Java Thread Affinity。
Java Thread Affinity是一个用于将JA代码中的线程绑定到特定CPU的技术。这种技术可以帮助开发者更好地利用底层硬件资源,提高程序的性能。为了实现这一功能,Java Thread Affinity会使用JNI或更为便利的JNA技术进行底层的交互。JNA是一个在JNI的基础上进行改进的库,可以更为方便地实现与native代码的交互。
先来了解CPU的几个关键概念:CPU、CPU socket和CPU core。CPU是处理器,是处理任务的核心部件。而CPU socket是安装CPU的插槽。随着技术的发展,多核CPU成为主流,一个CPU中可以包含多个core,而每个core才是真正的执行单元。
如果你是在linux系统上,可以通过lscpu命令查看系统的CPU情况。在输出信息中,我们可以看到服务器的socket数量、每个socket的core数量以及每个core可以同时处理的线程数等信息。
Java Thread Affinity提供了一个CpuLayout接口来与这些信息对应,这个接口包含了获取CPU数量、socket数量、core数量、线程数量以及将CPU ID转换为socket ID、core ID和线程ID等方法。
根据CPU layout的信息,AffinityStrategies提供了一系列的Affinity策略来安排不同线程之间的分布关系。这些策略包括SAME_CORE(运行在同一core中)、SAME_SOCKET(运行在同一socket中但不在同一core上)、DIFFERENT_SOCKET(运行在不同的socket中)等。这些策略都是根据CpuLayout的socketId和coreId来进行区分的。
接下来我们看一下AffinityLock的使用。我们可以获取一个CPU的lock。在JA7之前和之后的写法略有不同,但核心思想是一致的。acquireLock方法可以为线程获取任何可用的cpu,这是一个粗粒度的lock。如果我们想要获得更细粒度的control,可以使用acquireCore方法。acquireLock还有一个bind参数,表示是否将当前线程绑定到获得的cpu lock上。
除了上述的AffinityLock,还可以使用AffinityThreadFactory来为线程池中的线程设置AffinityStrategy。例如,我们可以创建一个线程池并为其设置多个AffinityStrategy来指定线程的绑定策略。最后通过AffinityLock的dumpLocks方法来查看当前CPU和thread的绑定状态。同时我们还可以直接通过API来分配CPU给特定的线程,具体是通过AffinitySupport的setAffinity方法来设置特定线程的CPU亲和性。
