Android C++多线程、生产者和消费者模型、JNI的使用

一、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();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值