Android jni 线程同步
Posted Alex_MaHao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android jni 线程同步相关的知识,希望对你有一定的参考价值。
文章目录
概述
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 /* 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层
启动四个线程,调用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 <thread>
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<decltype(g_mutex)> 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 < 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);
++i;
(*env).MonitorExit(lock);
return 0;
将mLock
对象传入,这样就实现了c++和java加同一把锁。
以上是关于Android jni 线程同步的主要内容,如果未能解决你的问题,请参考以下文章
JNI——Android Native中跨线程使用JNI的问题