通过JNI调用线程执行


我们知道taskset命令可以将进程绑定到某个或某几个cpu上执行。(不知道的google哦)

在某些情况下,绑定到1个cpu比多个cpu执行更快(上下文切换,resume、yield)

so,萌生了一个想法,Java中通过JNI指定线程在第几个cpu上执行。

这篇文章,首先要做的是通过JNI调用线程执行。

JNI调用可以参考这篇文章 Linux下测试Java的JNI(Java Native Interface)

我的系统和作者是一样的Ubuntu 12.04,但是采用文中提到的gcc编译动态链接库的方式,生成.so后,通过Java调用一直会报错“symbol lookup error: libcpu.so: undefined symbol: _Znwj”

咨询好友林克后,他给出了编译方式。

g++ -std=c++0x -I/home/jjf/hadoop/jdk1.6.0_45/include/linux/ -I/home/jjf/hadoop/jdk1.6.0_45/include/ -O0 -g3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"SetCpu.d" -MT"SetCpu.d" -o "SetCpu.o" "SetCpu.cpp"

g++ -shared -o "libcpu.so" ./SetCpu.o

其中/home/jjf/hadoop/jdk1.6.0_45是JDK安装目录

然后将生成的libcpu.so移到$java.library.path目录下

至于如何调用线程执行呢,参考了interface java with C timer library using JNI

感谢强大的stackoverflow!!!!! 不解释

SetCpu.java

public class SetCpu {
    static {
        System.loadLibrary("cpu");
    }
    public static native int getCpu(Runnable r);
}

CpuMain.java

public class CpuMain {
    public static void main(String[] args) {
        System.out.println(SetCpu.getCpu(new Runnable() {
            
            @Override
            public void run() {
                System.out.println("cpu");
            }
        }));
    }
}

SetCpu.h 是通过命令

javah -jni SetCpu
生成的

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class SetCpu */

#ifndef _Included_SetCpu
#define _Included_SetCpu
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     SetCpu
 * Method:    getCpu
 * Signature: (Ljava/lang/Runnable;)I
 */
JNIEXPORT jint JNICALL Java_SetCpu_getCpu
  (JNIEnv *, jclass, jobject);

#ifdef __cplusplus
}
#endif
#endif

SetCpu.cpp

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "SetCpu.h"
/* Header for class SetCpu */
#ifdef __cplusplus
extern "C" {
#endif
struct ThreadParams
{
    JavaVM *jvm;
    jobject callback;
};

void *myfun(void *ptr)
{
    ThreadParams* p = reinterpret_cast(ptr);
    JavaVM *jvm = p->jvm;
    jobject callback = p->callback;
    free(p);

    JNIEnv *env = NULL;
    jint res;
    res = jvm->AttachCurrentThread((void **)&env, NULL);
    if(res < 0)
    {
        fprintf(stderr, "Attach VM Thread failed\n");
        return NULL;
    }

    jclass RunnableInterface = env->GetObjectClass(callback);
    jmethodID Run = env->GetMethodID(RunnableInterface, "run","()V");
    env->CallVoidMethod(callback, Run);
    jvm->DetachCurrentThread();

    pthread_exit(NULL);
}

/*
 * Class:     SetCpu
 * Method:    getCpu
 * Signature: (Ljava/lang/Runnable;)I
 */
JNIEXPORT jint JNICALL Java_SetCpu_getCpu(JNIEnv *env, jclass obj, jobject r) {
    pthread_t tid;
    ThreadParams* ptr = new ThreadParams();
    JavaVM *jvm = NULL;

    env->GetJavaVM(&jvm);
    ptr->callback = env->NewGlobalRef(r);
    ptr->jvm = jvm;

    jint errInt = -1;
    jint sucInt = 1;
    if (pthread_create(&tid, NULL, myfun, reinterpret_cast(ptr)) != 0) {
        fprintf(stderr, "thread create failed\n");
        return errInt;
    }
    pthread_join(tid, NULL);
    return sucInt;
}

#ifdef __cplusplus
}
#endif

执行结果当然是 cpu 1


上篇: 一点点想法,关于Spark执行引擎大幅优化 下篇: Java线程绑定到具体的cpu上执行