今天写代码过程中,突然有个想法:我想知道Java线程执行在那个CPU核心上?或者说,我能控制我自己创建的线程运行在哪个CPU核心上?再或者说Java启动的线程和CPU核心的关系是什么样的,我能够自己定制吗?
或者有人问,你问这个问题有什么意思,这个又不用你关心。好吧,在Java平台上确实不用关心这些东西,你new一个Thread就可以直接运行了。现在的问题是我就想知道Java线程和CPU核心的执行关系,无关应用。
如果你确实想知道这个问题有什么用,我可以假设这么一个场景:已知自己创建的线程有两类,一类是IO密集型、一类是计算密集型,现在你想最大限度的使用CPU来分配这些线程,使得CPU的使用率最有效,现在我就可以分配CPU为一定数量的计算密集型和IO密集型,使得在每个CPU核心上均匀分配,而不是由系统分配;系统分配的话有可能会造成分布不均,使得CPU没有能够充分使用。
下面根据本文的成文思路开始叙述。
Java运行在虚拟机(Java Visual Machine, JVM)上,最有可能的就是JVM提供了这样的方法。对于JVM的运行状态,由xxxxMXBean表示,得到xxxxMXBean的方法有Java的ManagementFactory提供,具体ManagementFactory的类包戳这里
ManagementFactory提供了多个获得JVM运行状态的类:
public static ClassLoadingMXBean getClassLoadingMXBean()
public static MemoryMXBean getMemoryMXBean()
public static ThreadMXBean getThreadMXBean()
public static RuntimeMXBean getRuntimeMXBean()
public static CompilationMXBean getCompilationMXBean()
public static OperatingSystemMXBean getOperatingSystemMXBean()
public static List getMemoryPoolMXBeans()
public static List getMemoryManagerMXBeans()
public static List getGarbageCollectorMXBeans()
其中返回的这些个MXBean,包括ClassLoadingMXBean、 MemoryMXBean、 ThreadMXBean、 RuntimeMXBean、CompilationMXBean、OperatingSystemMXBean、MemoryPoolMXBean、MemoryManagerMXBean、GarbageCollectorMXBean,根据名字,也能得到相应的含义,进而推断出这些Bean可能会具有的方法;比如OperatingSystemMXBean,能够得到关于操作系统的信息,其实也就仅限于name、arch、version、availableProcessors、systemLoad等信息,没有再多的信息,这些信息都是JVM可以拿到的信息。其中和我们问题最为相关的是ThreadMXBean。
那么ThreadMXBean中有哪些方法可能和我们的问题相关呢?我把ThreadMXBean方法全部列在这里:
public int getThreadCount();
public int getPeakThreadCount();
public long getTotalStartedThreadCount();
public int getDaemonThreadCount();
public long[] getAllThreadIds();
public ThreadInfo getThreadInfo(long id);
public ThreadInfo[] getThreadInfo(long[] ids);
public ThreadInfo getThreadInfo(long id, int maxDepth);
public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth);
public boolean isThreadContentionMonitoringSupported();
public boolean isThreadContentionMonitoringEnabled();
public void setThreadContentionMonitoringEnabled(boolean enable);
public long getCurrentThreadCpuTime();
public long getCurrentThreadUserTime();
public long getThreadCpuTime(long id);
public long getThreadUserTime(long id);
public boolean isThreadCpuTimeSupported();
public boolean isCurrentThreadCpuTimeSupported();
public boolean isThreadCpuTimeEnabled();
public void setThreadCpuTimeEnabled(boolean enable);
public long[] findMonitorDeadlockedThreads();
public void resetPeakThreadCount();
public long[] findDeadlockedThreads();
public boolean isObjectMonitorUsageSupported();
public boolean isSynchronizerUsageSupported();
public ThreadInfo[] getThreadInfo(long[]ids, boolean lockedMonitors,boolean lockedSynchronizers);
public ThreadInfo[] dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers);
浏览下ThreadMXBean的方法,发现这个bean是将虚拟机启动的线程统一管理了。从该bean中可以得到启动线程的信息,发现和CPU没有什么关系。和CPU有关系的CPUTime获取和我们的问题毫无关系,注意想获取CPUTine需要isThreadCpuTimeSupported();看来JVM并没有给我们提供这样的方便。
在这块问题追查过程中,发现了一个比较有趣的问题,这个问题我以前也不甚清楚:对于启动一个JVM,JVM内部启动了几个线程呢?这个问题大家可以先考虑下,有时间的话我会在另外开文分析。
OK,也许是我知识量有限,求助于网络,看到stackoverflow上面的几个讨论,原来不只是我有这个疑问,大家也都对这个问题比较感兴趣。
http://stackoverflow.com/questions/974277/java-thread-running-on-which-processor
http://stackoverflow.com/questions/8956639/spin-a-processor-for-a-fixed-amount-of-cpu-time
http://stackoverflow.com/questions/2238272/java-thread-affinity
http://stackoverflow.com/questions/2758448/is-there-a-way-to-force-the-jvm-to-run-on-a-single-processor-or-core
看完这几篇讨论,结果应该呼之而出了。在Pure Java模型中,这个结果是No,至少对目前的JVM来说,这个功能没有支持,以后估计也不会支持。JVM的设计就是runs anywhere,屏蔽掉操作系统层面的概念,让你在JVM框架里完成自己需要的内容。而我的需求刚好是想通过JVM来控制我自己的运行程序运行在系统的哪个CPU核心上,这个应该是有违JVM设计的初衷,也许会在将来的高级API中提供这样的功能也说不定。
好吧,看到结果是No了,你肯定想离开了。不过我现在就是有这种优化要求,就是想将特定线程附加到特定的CPU上,怎么办?有什么变通的方法吗?
Pure Java不行,这个操作是操作系统层面的东西,我们需要调用操作系统的内容。这个首先想到的是JNI,没错,就是JNI。JNI(Java Native Interface)是sun(这个写Sun应该没错,尽管现在Java归属于Oracle)提供的java与系统中的原生方法交互的技术,使用JNI能帮助我们完成这个任务。这个任务对于C/C++来说就不是问题,将C/C++代码使用JNI调用,就能实现我们需要的功能。
这个线程附加到CPU核心的问题已经有人实现,看下github的这个地址:https://github.com/peter-lawrey/Java-Thread-Affinity,这个就能够实现我本文开始要求的功能,内容不错,推荐大家看看。
OK,本文到这里就算结束了,对于Java来说,处理常规的需求足够了,对于比较tricky and sticky的要求,就需要想办法解决,至少要知道其所以然。