The basic operation of multithreaded jni critical section (similar to the synchronized block java) process


Series of articles

1. Basic operation jni java layer to create native method, and generate a corresponding function jni
jni Properties Basic Operation 2. Operation of java
jni 3. Basic operation method of a java
jni 4. The basic operation of loading and unloading functions, dynamic registration and anti the method to register the local
jni 5. the basic operation of multithreaded critical section (similar to the synchronized block java) process


Foreword

In native code, java in order to achieve similar synchronizedfunctionality. That is a critical area, at the same time only one thread can operate.

synchronized(obj) {
	//这里就是临界区
}

Functions to achieve:
use <pthread.h>in pthread_create()creating a thread;
use "semaphore semaphore.h" is sem_wait()和sem_post()to wait and send a signal; or use "pthread.h" the mutex pthread_mutex_lock()和pthread_mutex_unlock()to achieve.

jni also provides functions similar function, that is MonitorEnter(jobject), and MonitorExit(jobject).

About jobject:
by jni.h, can know, jclass, jstring, jarray, jthrowable are all types jobject renamed.

Usually in the multi-threaded operating shared variables, such operations require the use of synchronous mutually exclusive.


Examples

Written in ThreadTest.cpp file. (Mainly env->written convenience ah ^ _ ^)

#include <jni.h>
#include <pthread.h>   //线程
#include <malloc.h>
#include <unistd.h>
#include <cstdlib> //c stdlib.h
#include <ctime> //c time.h
#include <cstdio> //c stdio.h

#include <android/log.h>
#ifndef LOG_TAG
#define LOG_TAG "stone.stone"
#define slogd(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#endif

struct Args3 {
    JavaVM* vm;
    int tid;
};

void* threadFun3(void* args); //线程函数;声明在前,方便test()使用。

void test(JavaVM *vm) {
	 pthread_t pid;
	 for(int i = 0; i < 10; i++) {
        struct Args3 *args = (Args3 *)(malloc(sizeof(struct Args3)));
        args->vm = vm;
        args->tid = i;

        //像如下这样传递,运行还是会报错的,是因为,指针变量的空间太小
//        struct Args3 args3 = {vm, i};
//        struct Args3* args = &args3;

        pthread_create(&pid, nullptr, threadFun3, args);
    }
}

static int count = 0;
void* threadFun3(void* arg) {
    Args3* args = (struct Args3 *)arg;
    JavaVM* vm = args->vm;

    JNIEnv *env = nullptr;
    int status = vm->GetEnv((void **)(&env), JNI_VERSION_1_6);
    if (status < 0) {
        slogd("stone->get env error. get 不到 env,就需要 attach");
        status = vm->AttachCurrentThread(&env, nullptr);
        if (status < 0) {
            slogd("stone->AttachCurrentThread error");
            return nullptr;
        } else {
            slogd("stone->AttachCurrentThread success");
        }
    }

    env->MonitorEnter(g_ObjCall); //进入监视
    
    int tid = args->tid;
    time_t t = time(nullptr) + count;
    srand(t);//设置随机因子,不同的随机因子,生成的随机数才不同
    sleep(rand() % 3); //随机数 取模,再线程睡眠 0~2秒
    count++;
    slogd("stone->threadFun3. tid=%d, count=%d, time=%ld", tid, count, t);
    
    env->MonitorExit(g_ObjCall); //退出监视
    
    return nullptr;
}


In another major cpp file:

#include "ThreadTest.cpp"

extern jobject g_ObjCall;//可以被其它地方使用
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
	JNIEnv* env;
    if (JNI_OK != vm->GetEnv(reinterpret_cast<void**> (&env),JNI_VERSION_1_6)) {
        slogd("JNI_OnLoad could not get JNI env");
        return JNI_ERR;
    }
	//jclass clazz = env->FindClass("com/stone/ndk/jni/JniActivity");
	//g_ObjCall = env->NewGlobalRef(env->AllocObject(clazz));
	g_ObjCall = env->NewGlobalRef(env->NewIntArray(1));
	
	test(vm);
	return JNI_VERSION_1_6;
}

In the present embodiment, the mutex efficient operation, then the result is output, in most cases, greater than the overall run time was 2 seconds; if not valid, that is, the multi-threaded parallel operation, overall operation time of 2 seconds or over.


Problems encountered and Precautions

  1. Thread function to transfer more than two parameters, required structure, and the structure is passed a pointer that require dynamic memory it. When the non-dynamic open storage space pointer variable will be insufficient, and collapse.
  2. test () parameter of vm, rewriting is JNI_OnLoad(JavaVM* vm, void* reserved)obtained when
  3. Why need to pass into the thread function JavaVM*parameters:
    Due JniEnv*unable threads share, in the child need to obtain a separate thread, it is through the acquisition JavaVM*of.
    First vm->GetEnv(), if the right to acquire, it means that the thread has been a subsidiary;
    otherwise, through the vm->AttachCurrentThread(&env, NULL)subsidiary threads, get the env
  4. Why use the global reference variable g_ObjCall:
    First of all, MonitorEnter(jobject)and MonitorExit(jobject)these two functions should be operated with a jobjectvariable type.
    To create jobject in the thread function, that first thought was like this static jobject jobject1 = env->NewIntArray(1);to build a static reference variable; but the operation will find crash, static variables are not enough to create a reference.
    The global reference variable, is to meet the requirements.
  5. I tried, within the thread function, through env->FindClass("classpath"), and then through env->AllocObject(jclass)to create the jobjectvariable.
    Run found env->FindClass()collapsed when.
    Because the child thread env, can not get directly to a custom class, jdk in class can be obtained. Even through the global reference jclassis created jobject, it will also collapse.
  6. Thread exit:
    when conditions are customized to meet, you can pthread_exit()exit the thread, before exiting the first exit monitor note that the callenv->MonitorExit()
  7. Have thought, java thread is created, MonitorEnter(jobject)and MonitorExit(jobject)can also be used to synchronize mutually exclusive? No attempt.

Published 400 original articles · won praise 364 · Views 1.62 million +

Guess you like

Origin blog.csdn.net/jjwwmlp456/article/details/89389725