一、Android C++ 线程简介
我们都知道Android是基于Linux内核的,而Linux是遵循POSIX线程标准的,POSIX线程库中有一系列Pthreads API方便我们对Linux线程的操作。所以我们在Android中使用C/C++线程也就转到了使用POSIX线程库。他们都在头文件“pthread.h”中。
- 创建子线程
1、pthread_t:用于声明一个线程对象如:pthread_t thread;
2、pthread_creat :用于创建一个实际的线程如:pthread_create(
&pthread,NULL,threadCallBack,NULL);其总共接收4个参数,第一
个参数为pthread_t对象,第二个参数为线程的一些属性我们一般传
NULL就行,第三个参数为线程执行的函数( void* threadCallBack(
void data) ),第四个参数是传递给线程的参数是void类型的既可
以传任意类型。
3、pthread_exit :用于退出线程如:pthread_exit(&thread),参数也
可以传NULL。注:线程回调函数最后必须调用此方法,不然APP
会退出(挂掉)。
4、创建JNI线程示例
//导入头文件
#include <pthread.h>
//创建一个线程实例
//声明一个线程对象
pthread_t pthread;
//函数指针
void *normalCallBack(void * data)
{
LOGD("create normal thread from C++!");
//退出线程
pthread_exit(&pthread);
}
//在java中定义了一个native方法,然后在JNI层实现它。
extern "C"
JNIEXPORT void JNICALL
Java_com_wq_jnithread_ThreadDemo_normalThread(JNIEnv *env, jobject thiz) {
// TODO: implement normalThread()
LOGD("normalThread !");
//创建一个实际的线程
pthread_create(&pthread, NULL, normalCallBack, NULL);
}
二、生产者和消费者模型
- pthread_mutex_t :用于创建线程锁对象如:pthread_mutex_t mutex;
-pthread_mutex_init :用于初始化pthread_mutex_t锁对象如:
pthread_mutex_init(&mutex, NULL); - pthread_mutex_destroy :用于销毁pthread_mutex_t锁对象如:
pthread_mutex_destroy(&mutex); - pthread_cond_t :用于创建线程条件对象如:pthread_cond_t cond;
- pthread_cond_init :用于初始化pthread_cond_t条件对象如:
pthread_cond_init(&cond, NULL); - pthread_cond_destroy :用于销毁pthread_cond_t条件对象如:
pthread_cond_destroy(&cond); - pthread_mutex_lock :用于上锁mutex,本线程上锁后的其他变量是不能
被别的线程操作的如:pthread_mutex_lock(&mutex); - pthread_mutex_unlock :用于解锁mutex,解锁后的其他变量可以被其他线程操作如:pthread_mutex_unlock(&mutex);
- pthread_cond_signal :用于发出条件信号如:pthread_cond_signal(&mutex, &cond);
- pthread_cond_wait :用于线程阻塞等待,直到pthread_cond_signal发出条件信号后才执行退出线程阻塞执行后面的操作。
- 代码示例如下
//生产者和消费者
//导入队列头文件
#include "queue"
#include "unistd.h"
//定义生产者线程
pthread_t product;
//定义消费者线程
pthread_t custom;
//创建线程锁对象
pthread_mutex_t mutex;
//创建线程条件对象
pthread_cond_t cond;
//创建一个队列
std::queue<int> queue;
//生产者函数指针
void *producCallback(void *data)
{
while (1)
{
//上锁mutex,本线程上锁后的其他变量是不能被别的线程操作的
pthread_mutex_lock(&mutex);
queue.push(1);
LOGD("生产者生产一个产品,通知消费者消费, 产品数量为 %d", queue.size());
//发出条件信号
pthread_cond_signal(&cond);
//解锁mutex,解锁后的其他变量可以被其他线程操作
pthread_mutex_unlock(&mutex);
//休眠5秒
sleep(5);
}
pthread_exit(&product);
}
void *customCallback(void *data)
{
while (1)
{
//上锁mutex,本线程上锁后的其他变量是不能被别的线程操作的
pthread_mutex_lock(&mutex);
if(queue.size() > 0)
{
//队列中最靠前位置的元素拿掉,是没有返回值的void函数
queue.pop();
LOGD("消费者消费产品,产品数量还剩余 %d ", queue.size());
} else{
LOGD("没有产品可以消费, 等待中...");
pthread_cond_wait(&cond, &mutex);
}
//解锁mutex,解锁后的其他变量可以被其他线程操作
pthread_mutex_unlock(&mutex);
usleep(500 * 1000);
}
pthread_exit(&custom);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_wq_jnithread_ThreadDemo_mutexThread(JNIEnv *env, jobject thiz) {
// TODO: implement mutexThread()
for(int i = 0; i < 10; i++)
{
//队列中由于是先进先出,push即在队尾插入一个元素
queue.push(1);
}
//初始化pthread_mutex_t锁对象
pthread_mutex_init(&mutex,NULL);
//初始化pthread_cond_t条件对象
pthread_cond_init(&cond, NULL);
//创建生产者线程
pthread_create(&product, NULL, producCallback, NULL);
//创建消费者线程
pthread_create(&custom, NULL, customCallback, NULL);
}
三、C++全局调用Java方法
1、签名方法参数类型对应表:
2、通过命令获取native方法签名:在native所在类的class文件目录下,执行命令:
javap -s ***.class
3、C++主线程调用Java方法
- 根据jobject获取jclass
jclass clz = env->GetObjectClass(jobj);
if(!clz)
{
return;
}
- 获取jmethodid
jmid = env->GetMethodID(clz, "onError", "(ILjava/lang/String;)V");
if(!jmid){
return;
}
- 调用方法
jenv->CallVoidMethod(jobj, jmid, code, jmsg);
- 主线程调用方法
jstring jmsg = jenv->NewStringUTF(msg);
jenv->CallVoidMethod(jobj, jmid, code, jmsg);
jenv->DeleteLocalRef(jmsg);
4、C++子线程调用Java方法
- 由于JniEnv是线程相关的,所以子线程中不能使用创建线程的JniEnv;而
JVM是进程相关的,所以可以通过JVM来获取当前线程的JniEnv,然后就可以
调用Java的方法了。
NIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void* reserved)
{
JNIEnv *env;
jvm = vm;
if(vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
{
return -1;
}
return JNI_VERSION_1_6;
}
- 通过JVM获取JniEnv
JNIEnv *env;
//当在一个线程里面调用AttachCurrentThread后,如果不需要用的时候一定要DetachCurrentThread,否则线程无法正常退出
jvm->AttachCurrentThread(&env, 0);
jstring jmsg = env->NewStringUTF(msg);
env->CallVoidMethod(jobj, jmid, code, jmsg);
env->DeleteLocalRef(jmsg);
jvm->DetachCurrentThread();