文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android jni 线程同步

2022-06-06 13:57

关注

文章目录概述问题示例c++层java层结果解决办法java层加锁c++层加锁java层和c++层共用一个锁 概述

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

先说结论:

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

问题示例 c++层

提供两个

native
方法,分别是
add
get

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

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

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

java层

启动四个线程,调用

native
add
方法共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 
std::mutex g_mutex;
extern "C" JNIEXPORT jstring JNICALL
Java_com_spearbothy_jnidemo_MainActivity_add(
        JNIEnv *env,
        jobject ) {
    g_mutex.lock();
    ++i;
    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 ) {
//    g_mutex.lock();
    std::lock_guard lock(g_mutex);
    ++i;
//    g_mutex.unlock();
    return 0;
}

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

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

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

修改

logNative()
,分别加锁。

 private void logNative() {
        index++;
        for (int i = 0; i  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);
    ++i;
    (*env).MonitorExit(lock);
    return 0;
}

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


作者:Alex_MaHao


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯