您好,登錄后才能下訂單哦!
這篇“Java中怎么將線程綁定到特定的CPU”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“Java中怎么將線程綁定到特定的CPU”文章吧。
java thread Affinity是用來(lái)將JAVA代碼中的線程綁定到CPU特定的核上,用來(lái)提升程序運(yùn)行的性能。
很顯然,要想和底層的CPU進(jìn)行交互,java thread Affinity一定會(huì)用到JAVA和native方法進(jìn)行交互的方法,JNI雖然是JAVA官方的JAVA和native方法進(jìn)行交互的方法,但是JNI在使用起來(lái)比較繁瑣。所以java thread Affinity實(shí)際使用的是JNA,JNA是在JNI的基礎(chǔ)上進(jìn)行改良的一種和native方法進(jìn)行交互的庫(kù)。
先來(lái)介紹CPU中幾個(gè)概念,分別是CPU,CPU socket和CPU core。
首先是CPU,CPU的全稱就是central processing unit,又叫做中央處理器,就是用來(lái)進(jìn)行任務(wù)處理的關(guān)鍵核心。
那么什么是CPU socket呢?所謂socket就是插CPU的插槽,如果組裝過(guò)臺(tái)式機(jī)的同學(xué)應(yīng)該都知道,CPU就是安裝在Socket上的。
CPU Core指的是CPU中的核數(shù),在很久之前CPU都是單核的,但是隨著多核技術(shù)的發(fā)展,一個(gè)CPU中可以包含多個(gè)核,而CPU中的核就是真正的進(jìn)行業(yè)務(wù)處理的單元。
如果你是在linux機(jī)子上,那么可以通過(guò)使用lscpu命令來(lái)查看系統(tǒng)的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
從上面的輸出我們可以看到,這個(gè)服務(wù)器有一個(gè)socket,每個(gè)socket有一個(gè)core,每個(gè)core可以同時(shí)處理1個(gè)線程。
這些CPU的信息可以稱為CPU layout。在linux中CPU的layout信息是存放在/proc/cpuinfo中的。
在Java Thread Affinity中有一個(gè)CpuLayout接口用來(lái)和這些信息進(jìn)行對(duì)應(yīng):
public interface CpuLayout { int cpus(); int sockets(); int coresPerSocket(); int threadsPerCore(); int socketId(int cpuId); int coreId(int cpuId); int threadId(int cpuId); }
根據(jù)CPU layout的信息, AffinityStrategies提供了一些基本的Affinity策略,用來(lái)安排不同的thread之間的分布關(guān)系,主要有下面幾種:
SAME_CORE - 運(yùn)行在同一個(gè)core中。 SAME_SOCKET - 運(yùn)行在同一個(gè)socket中,但是不在同一個(gè)core上。 DIFFERENT_SOCKET - 運(yùn)行在不同的socket中 DIFFERENT_CORE - 運(yùn)行在不同的core上 ANY - 任何情況都可以
這些策略也都是根據(jù)CpuLayout的socketId和coreId來(lái)進(jìn)行區(qū)分的,我們以SAME_CORE為例,按下它的具體實(shí)現(xiàn):
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策略可以有順序,在前面的策略會(huì)首先匹配,如果匹配不上則會(huì)選擇第二策略,依此類推。
接下來(lái)我們看下Affinity的具體使用,首先是獲得一個(gè)CPU的lock,在JAVA7之前,我們可以這樣寫(xiě):
AffinityLock al = AffinityLock.acquireLock(); try { // do some work locked to a CPU. } finally { al.release(); }
在JAVA7之后,可以這樣寫(xiě):
try (AffinityLock al = AffinityLock.acquireLock()) { // do some work while locked to a CPU. }
acquireLock方法可以為線程獲得任何可用的cpu。這個(gè)是一個(gè)粗粒度的lock。如果想要獲得細(xì)粒度的core,可以用acquireCore:
try (AffinityLock al = AffinityLock.acquireCore()) { // do some work while locked to a CPU. }
acquireLock還有一個(gè)bind參數(shù),表示是否將當(dāng)前的線程綁定到獲得的cpu lock上,如果bind參數(shù)=true,那么當(dāng)前的thread會(huì)在acquireLock中獲得的CPU上運(yùn)行。如果bind參數(shù)=false,表示acquireLock會(huì)在未來(lái)的某個(gè)時(shí)候進(jìn)行bind。
上面我們提到了AffinityStrategy,這個(gè)AffinityStrategy可以作為acquireLock的參數(shù)使用:
public AffinityLock acquireLock(AffinityStrategy... strategies) { return acquireLock(false, cpuId, strategies); }
通過(guò)調(diào)用當(dāng)前AffinityLock的acquireLock方法,可以為當(dāng)前的線程分配和之前的lock策略相關(guān)的AffinityLock。
AffinityLock還提供了一個(gè)dumpLocks方法,用來(lái)查看當(dāng)前CPU和thread的綁定狀態(tài)。我們舉個(gè)例子:
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<Void>() { @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);
上面的代碼中,我們創(chuàng)建了一個(gè)4個(gè)線程的線程池,對(duì)應(yīng)的ThreadFactory是AffinityThreadFactory,給線程池起名bg,并且分配了3個(gè)AffinityStrategy。 意思是首先分配到同一個(gè)core上,然后到不同的socket上,最后是任何可用的CPU。
然后具體執(zhí)行的過(guò)程中,我們提交了12個(gè)線程,但是我們的Thread pool最多只有4個(gè)線程,可以預(yù)見(jiàn), AffinityLock.dumpLocks方法返回的結(jié)果中只有4個(gè)線程會(huì)綁定CPU,一起來(lái)看看:
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
從輸出結(jié)果可以看到,CPU0是不可用的。其他7個(gè)CPU是可用的,但是只綁定了4個(gè)線程,這和我們之前的分析是匹配的。
接下來(lái),我們把AffinityThreadFactory的AffinityStrategy修改一下,如下所示:
new AffinityThreadFactory("bg", SAME_CORE)
表示線程只會(huì)綁定到同一個(gè)core中,因?yàn)樵诋?dāng)前的硬件中,一個(gè)core同時(shí)只能支持一個(gè)線程的綁定,所以可以預(yù)見(jiàn)最后的結(jié)果只會(huì)綁定一個(gè)線程,運(yùn)行結(jié)果如下:
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
可以看到只有第一個(gè)線程綁定了CPU,和之前的分析相匹配。
上面我們提到的AffinityLock的acquireLock方法其實(shí)還可以接受一個(gè)CPU id參數(shù),直接用來(lái)獲得傳入CPU id的lock。這樣后續(xù)線程就可以在指定的CPU上運(yùn)行。
public static AffinityLock acquireLock(int cpuId) { return acquireLock(true, cpuId, AffinityStrategies.ANY); }
實(shí)時(shí)上這種Affinity是存放在BitSet中的,BitSet的index就是cpu的id,對(duì)應(yīng)的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.
注意,因?yàn)锽itSet底層是用Long來(lái)進(jìn)行數(shù)據(jù)存儲(chǔ)的,所以這里的index是bit index,所以我們需要對(duì)十進(jìn)制的CPU index進(jìn)行轉(zhuǎn)換。
以上就是關(guān)于“Java中怎么將線程綁定到特定的CPU”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。