网易首页 > 网易号 > 正文 申请入驻

java高级用法之:绑定CPU的线程Thread-Affinity

0
分享至

简介

在现代计算机系统中,可以有多个CPU,每个CPU又可以有多核。为了充分利用现代CPU的功能,JAVA中引入了多线程,不同的线程可以同时在不同CPU或者不同CPU核中运行。但是对于JAVA程序猿来说创建多少线程是可以自己控制的,但是线程到底运行在哪个CPU上,则是一个黑盒子,一般来说很难得知。

但是如果是不同CPU核对同一线程进行调度,则可能会出现CPU切换造成的性能损失。一般情况下这种损失是比较小的,但是如果你的程序特别在意这种CPU切换带来的损耗,那么可以试试今天要讲的Java Thread Affinity.

Java Thread Affinity简介

java thread Affinity是用来将JAVA代码中的线程绑定到CPU特定的核上,用来提升程序运行的性能。

很显然,要想和底层的CPU进行交互,java thread Affinity一定会用到JAVA和native方法进行交互的方法,JNI虽然是JAVA官方的JAVA和native方法进行交互的方法,但是JNI在使用起来比较繁琐。所以java thread Affinity实际使用的是JNA,JNA是在JNI的基础上进行改良的一种和native方法进行交互的库。

先来介绍CPU中几个概念,分别是CPU,CPU socket和CPU core。

首先是CPU,CPU的全称就是central processing unit,又叫做中央处理器,就是用来进行任务处理的关键核心。

那么什么是CPU socket呢?所谓socket就是插CPU的插槽,如果组装过台式机的同学应该都知道,CPU就是安装在Socket上的。

CPU Core指的是CPU中的核数,在很久之前CPU都是单核的,但是随着多核技术的发展,一个CPU中可以包含多个核,而CPU中的核就是真正的进行业务处理的单元。

如果你是在linux机子上,那么可以通过使用lscpu命令来查看系统的CPU情况,如下所示:

Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 1
On-line CPU(s) list: 0
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 94
Model name: Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz
Stepping: 3
CPU MHz: 2400.000
BogoMIPS: 4800.00
Hypervisor vendor: KVM
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 4096K
L3 cache: 28160K
NUMA node0 CPU(s): 0
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single fsgsbase bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 arat

从上面的输出我们可以看到,这个服务器有一个socket,每个socket有一个core,每个core可以同时处理1个线程。

这些CPU的信息可以称为CPU layout。在linux中CPU的layout信息是存放在/proc/cpuinfo中的。

在Java Thread Affinity中有一个CpuLayout接口用来和这些信息进行对应:

public interface CpuLayout {

int cpus();

int sockets();

int coresPerSocket();

int threadsPerCore();

int socketId(int cpuId);

int coreId(int cpuId);

int threadId(int cpuId);
}

根据CPU layout的信息, AffinityStrategies提供了一些基本的Affinity策略,用来安排不同的thread之间的分布关系,主要有下面几种:

SAME_CORE - 运行在同一个core中。
SAME_SOCKET - 运行在同一个socket中,但是不在同一个core上。
DIFFERENT_SOCKET - 运行在不同的socket中
DIFFERENT_CORE - 运行在不同的core上
ANY - 任何情况都可以

这些策略也都是根据CpuLayout的socketId和coreId来进行区分的,我们以SAME_CORE为例,按下它的具体实现:

SAME_CORE {
@Override
public boolean matches(int cpuId, int cpuId2) {
CpuLayout cpuLayout = AffinityLock.cpuLayout();
return cpuLayout.socketId(cpuId) == cpuLayout.socketId(cpuId2) &&
cpuLayout.coreId(cpuId) == cpuLayout.coreId(cpuId2);

Affinity策略可以有顺序,在前面的策略会首先匹配,如果匹配不上则会选择第二策略,依此类推。

AffinityLock的使用

接下来我们看下Affinity的具体使用,首先是获得一个CPU的lock,在JAVA7之前,我们可以这样写:

AffinityLock al = AffinityLock.acquireLock();
try {
// do some work locked to a CPU.
} finally {
al.release();

在JAVA7之后,可以这样写:

try (AffinityLock al = AffinityLock.acquireLock()) {
// do some work while locked to a CPU.

acquireLock方法可以为线程获得任何可用的cpu。这个是一个粗粒度的lock。如果想要获得细粒度的core,可以用acquireCore:

try (AffinityLock al = AffinityLock.acquireCore()) {
// do some work while locked to a CPU.

acquireLock还有一个bind参数,表示是否将当前的线程绑定到获得的cpu lock上,如果bind参数=true,那么当前的thread会在acquireLock中获得的CPU上运行。如果bind参数=false,表示acquireLock会在未来的某个时候进行bind。

上面我们提到了AffinityStrategy,这个AffinityStrategy可以作为acquireLock的参数使用:

public AffinityLock acquireLock(AffinityStrategy... strategies) {
return acquireLock(false, cpuId, strategies);

通过调用当前AffinityLock的acquireLock方法,可以为当前的线程分配和之前的lock策略相关的AffinityLock。

AffinityLock还提供了一个dumpLocks方法,用来查看当前CPU和thread的绑定状态。我们举个例子:

private static final ExecutorService ES = Executors.newFixedThreadPool(4,
new AffinityThreadFactory("bg", SAME_CORE, DIFFERENT_SOCKET, ANY));

for (int i = 0; i < 12; i++)
ES.submit(new Callable() {
@Override
public Void call() throws InterruptedException {
Thread.sleep(100);
return null;
}
});
Thread.sleep(200);
System.out.println("\nThe assignment of CPUs is\n" + AffinityLock.dumpLocks());
ES.shutdown();
ES.awaitTermination(1, TimeUnit.SECONDS);

上面的代码中,我们创建了一个4个线程的线程池,对应的ThreadFactory是AffinityThreadFactory,给线程池起名bg,并且分配了3个AffinityStrategy。意思是首先分配到同一个core上,然后到不同的socket上,最后是任何可用的CPU。

然后具体执行的过程中,我们提交了12个线程,但是我们的Thread pool最多只有4个线程,可以预见, AffinityLock.dumpLocks方法返回的结果中只有4个线程会绑定CPU,一起来看看:

The assignment of CPUs is
0: CPU not available
1: Reserved for this application
2: Reserved for this application
3: Reserved for this application
4: Thread[bg-4,5,main] alive=true
5: Thread[bg-3,5,main] alive=true
6: Thread[bg-2,5,main] alive=true
7: Thread[bg,5,main] alive=true

从输出结果可以看到,CPU0是不可用的。其他7个CPU是可用的,但是只绑定了4个线程,这和我们之前的分析是匹配的。

接下来,我们把AffinityThreadFactory的AffinityStrategy修改一下,如下所示:

new AffinityThreadFactory("bg", SAME_CORE)

表示线程只会绑定到同一个core中,因为在当前的硬件中,一个core同时只能支持一个线程的绑定,所以可以预见最后的结果只会绑定一个线程,运行结果如下:

The assignment of CPUs is
0: CPU not available
1: Reserved for this application
2: Reserved for this application
3: Reserved for this application
4: Reserved for this application
5: Reserved for this application
6: Reserved for this application
7: Thread[bg,5,main] alive=true

可以看到只有第一个线程绑定了CPU,和之前的分析相匹配。

使用API直接分配CPU

上面我们提到的AffinityLock的acquireLock方法其实还可以接受一个CPU id参数,直接用来获得传入CPU id的lock。这样后续线程就可以在指定的CPU上运行。

public static AffinityLock acquireLock(int cpuId) {
return acquireLock(true, cpuId, AffinityStrategies.ANY);

实时上这种Affinity是存放在BitSet中的,BitSet的index就是cpu的id,对应的value就是是否获得锁。

先看下setAffinity方法的定义:

public static void setAffinity(int cpu) {
BitSet affinity = new BitSet(Runtime.getRuntime().availableProcessors());
affinity.set(cpu);
setAffinity(affinity);

再看下setAffinity的使用:

long currentAffinity = AffinitySupport.getAffinity();
Affinity.setAffinity(1L << 5); // lock to CPU 5.

注意,因为BitSet底层是用Long来进行数据存储的,所以这里的index是bit index,所以我们需要对十进制的CPU index进行转换。
总结

Java Thread Affinity可以从JAVA代码中对程序中Thread使用的CPU进行控制,非常强大,大家可以运用起来。

本文已收录于 http://www.flydean.com/01-java-thread-affinity/

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
太奇怪了!姚明篮协主席,王芳篮协副主席,姚明怎么就没实权呢?

太奇怪了!姚明篮协主席,王芳篮协副主席,姚明怎么就没实权呢?

林子说事
2024-05-19 17:18:49
突然重大决定!13年,母队等了保罗整整13年……

突然重大决定!13年,母队等了保罗整整13年……

篮球实战宝典
2024-05-19 13:42:36
一直以为张雨绮属于微胖的,但这张照片里腿好细啊,真瘦啊

一直以为张雨绮属于微胖的,但这张照片里腿好细啊,真瘦啊

娱乐八卦木木子
2024-05-19 16:55:06
央视曝光!摩托车驾照一天下本,网友反问:骑电动车凭啥扣C证分

央视曝光!摩托车驾照一天下本,网友反问:骑电动车凭啥扣C证分

电动车的那些事儿
2024-05-18 06:20:09
全红婵家大变样,昔日两层农家小楼变模样,还装上豪华不锈钢大门

全红婵家大变样,昔日两层农家小楼变模样,还装上豪华不锈钢大门

尘语者
2024-05-17 17:32:56
爱沙尼亚总理指出乌克兰获得世界支持不足的原因

爱沙尼亚总理指出乌克兰获得世界支持不足的原因

老马拉车莫少装
2024-05-19 08:57:38
突发利空!又一家公司财务造假被ST,11万股东被埋,看看你踩雷没

突发利空!又一家公司财务造假被ST,11万股东被埋,看看你踩雷没

老道闲聊
2024-05-19 08:38:23
打一虎,惊万人!

打一虎,惊万人!

梳子姐
2024-05-19 11:34:26
男子被蜱虫咬几天后做足疗才发现,网友:建议把足疗纳入医保。

男子被蜱虫咬几天后做足疗才发现,网友:建议把足疗纳入医保。

荷兰豆爱健康
2024-05-19 12:36:41
中国曾三次忍辱负重“装孙子”,完美躲过美国制裁!最终迎来崛起

中国曾三次忍辱负重“装孙子”,完美躲过美国制裁!最终迎来崛起

猫眼观史
2024-05-16 18:11:46
马克龙的面子谁也不给啊!

马克龙的面子谁也不给啊!

随机耳洞
2024-05-19 10:36:48
张玉宁刚无缘中泰之战,伊万就火线敲定入替者,曾是巴甲王牌锋霸

张玉宁刚无缘中泰之战,伊万就火线敲定入替者,曾是巴甲王牌锋霸

罗掌柜体育
2024-05-19 19:41:19
徐梓钧发文回复:确实睡过、收过钱、享受资源,但没接受他的感情

徐梓钧发文回复:确实睡过、收过钱、享受资源,但没接受他的感情

娱乐圈酸柠檬
2024-05-19 17:05:37
知名品牌门店直播间画面低俗露骨,还对“擦边”评论“嗯嗯哈哈”!被无限期封号,公司道歉!去年大卖30多万辆

知名品牌门店直播间画面低俗露骨,还对“擦边”评论“嗯嗯哈哈”!被无限期封号,公司道歉!去年大卖30多万辆

每日经济新闻
2024-05-19 13:31:09
楼市政策彻底转灯,但中国经济再造奇迹取决于援乌亲美

楼市政策彻底转灯,但中国经济再造奇迹取决于援乌亲美

陶舜财经
2024-05-18 16:33:08
笑不活了!吴彦祖入驻抖音找不到账号疯狂求助,各地分祖现身支招

笑不活了!吴彦祖入驻抖音找不到账号疯狂求助,各地分祖现身支招

小咪侃娱圈
2024-05-19 14:50:37
扔掉一堆东西后,这6样东西我不会再囤了,个个是贫穷家庭的通病

扔掉一堆东西后,这6样东西我不会再囤了,个个是贫穷家庭的通病

美家指南
2024-05-17 08:03:57
深夜A股突发王炸利好,远超印花税,2亿股民的好日子要来了!

深夜A股突发王炸利好,远超印花税,2亿股民的好日子要来了!

静守时光落日
2024-05-19 13:20:52
中国经济的问题:追究下去,都归结于分配的问题

中国经济的问题:追究下去,都归结于分配的问题

永不出场的戈多
2024-05-18 10:56:35
开播2集,拿下榜单第一,优秀国产谍战剧再次来袭

开播2集,拿下榜单第一,优秀国产谍战剧再次来袭

娱乐圈酸柠檬
2024-05-19 18:49:30
2024-05-19 23:04:49
flydean程序那些事
flydean程序那些事
最通俗的解读,最深刻的干货!
356文章数 438关注度
往期回顾 全部

科技要闻

雷军直播开车2000万人围观!突然遭别车

头条要闻

俄法院下令:没收德意志银行资产

头条要闻

俄法院下令:没收德意志银行资产

体育要闻

欧文:我尽力不哭出来 我们还要走很远

娱乐要闻

《庆余年2》首播口碑出炉!有好有坏

财经要闻

洞庭湖区非法采砂 2000余亩洲滩被挖空

汽车要闻

智驾升级/月底上市 问界新M7 MAX焕新版

态度原创

艺术
教育
本地
时尚
公开课

艺术要闻

真诚度101%,35岁的诚品画廊为什么选择北京?

教育要闻

三年级,今年小明8岁,爸爸34岁,当两人年龄和72岁时,各几岁

本地新闻

博物馆的正确打开方式|来河南,沉浸式体验中原文明

去了成都才发现:太古里满街都是“吊带+低腰裤”,时髦又养眼

公开课

父亲年龄越大孩子越不聪明?

无障碍浏览 进入关怀版