Android jni 线程同步

Elaine ·
更新时间:2024-09-20
· 909 次阅读

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

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

先说结论:

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

问题示例 c++层

提供两个native方法,分别是addget

int i = 0; extern "C" JNIEXPORT jstring JNICALL Java_com_spearbothy_jnidemo_MainActivity_add( JNIEnv *env, jobject /* this */) { ++i; 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 std::mutex g_mutex; extern "C" JNIEXPORT jstring JNICALL Java_com_spearbothy_jnidemo_MainActivity_add( JNIEnv *env, jobject /* this */) { 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 /* this */) { // 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



线程同步 jni 线程 Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号