android中可以通过jni调用native的方法,那么如果在java中存在多个线程调用native的方法,它的展现形式是如何呢?

先说结论:

native的默认执行与java调用的线程保持一致,即处于同一个线程中。其次,如果多个线程调用native方法,也存在线程不安全的情况,需要解决。

提供两个 native 方法,分别是 add get

int i = 0;
extern "C" JNIEXPORT jstring JNICALL
Java_com_spearbothy_jnidemo_MainActivity_add(
        JNIEnv *env,
        jobject /* this */) {
    return 0;
extern "C" JNIEXPORT jstring JNICALL
Java_com_spearbothy_jnidemo_MainActivity_get(
        JNIEnv *env,
        jobject /* this */) {
    return env->NewStringUTF(to_string(i).c_str());

add主要做自增操作,由java起多个线程调用。

get主要是再结束之后获取结果,没有直接放到add中打印的原因是,android瞬间打印多个log,存在log丢失的现象。(后面有时间会找一下原因。)

java层

启动四个线程,调用nativeadd方法共40000次,并打印最终结果。

    private Handler mMainHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            cout++;
            if (cout > 3) {
                // 输出结果
                Log.i("jni", " c++ -> " + get());
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startMainLoop();
        startThreadLoop();
        startThreadLoop();
        startThreadLoop();
   private void startMainLoop() {
        logNative();
    private void startThreadLoop() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                logNative();
        }).start();
    private void logNative() {
        for (int i = 0; i < 10000; i++) {
            add();
        mMainHandler.sendEmptyMessage(1);
    public native void add();
    public native String get();

如果运行多次,可能存在线程不安全的情况,最终i的值不是40000

jni:  c++ -> 39670

java层加锁

logNative()add()前后加锁

 private void logNative() {
        for (int i = 0; i < 10000; i++) {
            synchronized (mLock) {
                add();
        mMainHandler.sendEmptyMessage(1);

此处加锁的方式有两种:

  • 一种是直接加在方法上,即private synchronized void .. (耗时20ms)
  • 还有一种更细粒度的。通过验证,更细粒度的执行效率会更低。因为频繁的加锁解锁。(耗时100ms)

c++层加锁

#include <thread>
std::mutex g_mutex;
extern "C" JNIEXPORT jstring JNICALL
Java_com_spearbothy_jnidemo_MainActivity_add(
        JNIEnv *env,
        jobject /* this */) {
    g_mutex.lock();
    g_mutex.unlock();
    return 0;

mutex是c++ 11 推出的相关功能。使用上和java的Lock十分相似。

经过验证,在c++1层加锁的效率更高(耗时26ms)。

c++也提供了不需要解锁的自动解锁机制。

extern "C" JNIEXPORT jstring JNICALL
Java_com_spearbothy_jnidemo_MainActivity_add(
        JNIEnv *env,
        jobject /* this */) {
//    g_mutex.lock();
    std::lock_guard<decltype(g_mutex)> lock(g_mutex);
//    g_mutex.unlock();
    return 0;

decltype是获取对象的类型,该处和java的泛型类似。

lock_guard在构造函数中自动加锁,在析构函数中解锁。

java层和c++层共用一个锁

修改logNative(),分别加锁。

 private void logNative() {
        index++;
        for (int i = 0; i < 10000; i++) {
            if (index > 3) {
                synchronized (mLock) {
                    add();
            } else {
                addLock(mLock);
        mMainHandler.sendEmptyMessage(1);
public native void addLock(Object lock);

mLock为锁对象,只在其中一个线程以java的方式进行加锁,同时另一部分在c++中加锁。

extern "C" JNIEXPORT jstring JNICALL
Java_com_spearbothy_jnidemo_MainActivity_addLock(
        JNIEnv *env,
        jobject lock) {
    (*env).MonitorEnter(lock);
    (*env).MonitorExit(lock);
    return 0;

mLock对象传入,这样就实现了c++和java加同一把锁。

概述android中可以通过jni调用native的方法,那么如果在java中存在多个线程调用native的方法,它的展现形式是如何呢?先说结论:native的默认执行与java调用的线程保持一致,即处于同一个线程中。其次,如果多个线程调用native方法,也存在线程不安全的情况,需要解决。问题示例c++层提供两个native方法,分别是add和getint i = 0;exte...
在开发中常常会遇到从Java层传递数据到JNI层,然后在JNI拿到数据后就可以用C语言进行操作了,操作完数据后通常还需要把处理后的数据传回Java层。下面分别进行小结。从Java层传到JNI层 使用GetByteArrayRegion的方式。 该方法的本质是将Java端数组数据拷贝到本地的数组中,所以在JNI对数据修改后Java端的数据并没有改变。 使用GetPrimitiveArrayCriti
该函数用于C函数的多线程编程中,互斥锁的初始化。 (类似 synchronized 作用) int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button;
今天来唠唠JNI中关于多线程。多线程,即多个线程同时工作,多线程的问题比较复杂,在实际情况下可能会面临众多的问题。JNI中也提供了一系列函数帮助我们完成多线程交互。博客内容大致分为如下:  1. 同步代码块  2. 等待唤醒 本篇内容可能介绍的比较浅显,更多的东西还需要大家再实战中去慢慢体会。 一、同步代码块 如果本地代码要运行在多个线程环境中,可能会面临同时共享资源的情况。
上一篇文章说到 JNIEnv 是一个与线程相关的变量,即线程A有一个 JNIEnv变量, 线程B也有一个JNIEnv变量,由于线程相关,所以A线程不能使用B线程的 JNIEnv 结构体变量。 问题描述: 一个java对象通过JNI调用DLL中一个send()函数向服务器发送消息,不等服务器消息到来就立即返回,同时把JNI接口的指针JNIEnv *env(虚拟机环境指针),和jobjec
在进行android的ndk开发时,耗时任务会用到native子线程。在pthread头文件中定义有创建子线程、互斥锁、条件变量等相关方法。线程同步是利用互斥锁(mutex)与条件(condition)变量的结合,经常出现于生产者与消费者模式场景中。先定义相关变量:#include &lt;pthread.h&gt; //生产者线程 pthread_t thread_product; //消费者线...
1. 在Android Studio中创建一个新的Android项目。 2. 在项目中创建一个名为“jni”的文件夹。 3. 在“jni”文件夹中创建一个名为“native-lib.cpp”的文件。 4. 在“native-lib.cpp”文件中编写您的C/C++代码。 5. 在“app”模块的“build.gradle”文件中添加以下代码: android { defaultConfig { externalNativeBuild { cmake { cppFlags "-std=c++11" abiFilters 'armeabi-v7a', 'x86' externalNativeBuild { cmake { path "CMakeLists.txt" 6. 在“jni”文件夹中创建一个名为“CMakeLists.txt”的文件,并添加以下代码: cmake_minimum_required(VERSION 3.4.1) add_library(native-lib SHARED native-lib.cpp) find_library(log-lib log) target_link_libraries(native-lib ${log-lib}) 7. 在Android Studio的“Terminal”中,使用以下命令编译您的代码: ./gradlew assembleDebug 8. 在您的Android项目中,您可以使用以下代码调用您的JNI函数: static { System.loadLibrary("native-lib"); public native String yourJniFunction(); 希望这些步骤能够帮助您使用JNI
Kotlin协程不可用(java.lang.IllegalStateException: Module with the Main dispatcher is missing)另一种可能原因 JNI的三种引用