Android-Jni线程(二)— 线程锁之生产者消费者

615 阅读6分钟
原文链接: blog.csdn.net

        上一篇博客《Android-Jni线程(一)— 创建线程》我们在Jni层创建了一个最简单的线程但啥事也没有做,总感觉没什么实际的作用,现在我们就来实现经典的“生产者和消费者”线程锁模型。先看看演示效果:



首先还是老规矩先讲讲大体步骤:

1、创建生产者和消费者2个线程:pthread_t   pthread_produc,pthread_customer。

2、创建线程锁和条件对象:pthread_mutex_t mutex和pthread_cond_t cond。

3、创建产品队列queue(C++中“queue”头文件)。

5、生产者定时生产产品往队列queue添加(push)产品,并通知(pthread_cond_signal)消费者可以消费了;消费者快速从queue取出(pop)产品消费,当queue中没有产品(size == 0)时,就阻塞等待(pthread_cond_wait)生产者生产产品后的通知并马上消费。

6、生产者添加产品(queue.push)和消费者消费产品(queue.pop)时都在线程锁中(pthread_mutex_lock)执行,当添加/消费产品后就解锁线程(pthread_mutex_unlock),这样就能保证同一时刻只有生产者或消费者才能对产品进行操作。

接下来Jni中用C++代码实现:

[cpp] view plain copy print?
  1. #include <jni.h>  
  2.   
  3. #include "AndroidLog.h"  
  4. #include "pthread.h"  
  5. #include "unistd.h"  
  6. #include "queue"  
  7.   
  8. //1、一般线程  
  9. pthread_t pthread;//线程对象  
  10. void *threadDoThings(void *data)  
  11. {  
  12.     LOGD("jni thread do things");  
  13.     pthread_exit(&pthread);  
  14. }  
  15.   
  16. extern "C"  
  17. JNIEXPORT void JNICALL  
  18. Java_com_ywl5320_jnithread_JniThread_normalThread(JNIEnv *env, jobject instance) {  
  19.     // TODO  
  20.     LOGD("normal thread");  
  21.     //创建线程  
  22.     pthread_create(&pthread, NULL, threadDoThings, NULL);  
  23.   
  24. }  
  25.   
  26.   
  27. //2、线程锁  
  28.   
  29. std::queue<int> queue; //产品队列,里面是int的队列  
  30. pthread_t pthread_produc; //生产者线程  
  31. pthread_t pthread_customer; //消费者线程  
  32. pthread_mutex_t mutex; //线程锁  
  33. pthread_cond_t cond; //条件对象  
  34.   
  35.   
  36. void *ptoducThread(void *data)  
  37. {  
  38.     while(queue.size() < 50)  
  39.     {  
  40.         LOGD("生产者生产一个产品");  
  41.         pthread_mutex_lock(&mutex); //操作队列前先加锁  
  42.         queue.push(1);  
  43.         if(queue.size() > 0)  
  44.         {  
  45.             LOGD("生产者通知消费者有产品产生,产品数量为:%d", queue.size());  
  46.             pthread_cond_signal(&cond); //有了产品通知消费者  
  47.         }  
  48.         pthread_mutex_unlock(&mutex); //解锁线程  
  49.         sleep(4); //休息4秒,单位是秒  
  50.     }  
  51.     pthread_exit(&pthread_produc);  
  52. }  
  53.   
  54. void *customerThread(void *data)  
  55. {  
  56.     char *prod = (char *) data;  
  57.     LOGD("%", prod);  
  58.     while(1) //这里用死循环,时间情况应该给一个变量来控制跳出循环  
  59.     {  
  60.         pthread_mutex_lock(&mutex); //操作队列前先加锁  
  61.         if(queue.size() > 0)  
  62.         {  
  63.             queue.pop();  
  64.             LOGE("消费者消费一个产品,产品数量为:%d", queue.size());  
  65.         } else{  
  66.             LOGE("产品消费完了,等待生产者生产......");  
  67.             pthread_cond_wait(&cond, &mutex); //阻塞线程等待生产者的通知  
  68.         }  
  69.         pthread_mutex_unlock(&mutex);//解锁线程  
  70.         usleep(500 * 1000); //休息0.5秒,usleep单位是微妙  
  71.     }  
  72.     pthread_exit(&pthread_customer);  
  73. }  
  74.   
  75. void initMutex()  
  76. {  
  77.     pthread_mutex_init(&mutex, NULL); //初始化锁对象 对应pthread_mutex_destroy销毁锁对象  
  78.     pthread_cond_init(&cond, NULL); //初始化条件变量 对应pthread_cond_destroy销毁条件变量  
  79.     pthread_create(&pthread_produc, NULL, ptoducThread, (void *) "product");  //创建生产者线程,并传递参数  
  80.     pthread_create(&pthread_customer, NULL, customerThread, NULL); //创建消费者线程  
  81. }  
  82.   
  83. extern "C"  
  84. JNIEXPORT void JNICALL  
  85. Java_com_ywl5320_jnithread_JniThread_mutexThread(JNIEnv *env, jobject instance) {  
  86.     // TODO  
  87.     //初始化时,先往队列中添加10个产品  
  88.     for(int i = 0; i < 10; i++)  
  89.     {  
  90.         queue.push(i);  
  91.     }  
  92.     initMutex();  
  93. }  
#include <jni.h>

#include "AndroidLog.h"
#include "pthread.h"
#include "unistd.h"
#include "queue"

//1、一般线程
pthread_t pthread;//线程对象
void *threadDoThings(void *data)
{
    LOGD("jni thread do things");
    pthread_exit(&pthread);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_ywl5320_jnithread_JniThread_normalThread(JNIEnv *env, jobject instance) {
    // TODO
    LOGD("normal thread");
    //创建线程
    pthread_create(&pthread, NULL, threadDoThings, NULL);

}


//2、线程锁

std::queue<int> queue; //产品队列,里面是int的队列
pthread_t pthread_produc; //生产者线程
pthread_t pthread_customer; //消费者线程
pthread_mutex_t mutex; //线程锁
pthread_cond_t cond; //条件对象


void *ptoducThread(void *data)
{
    while(queue.size() < 50)
    {
        LOGD("生产者生产一个产品");
        pthread_mutex_lock(&mutex); //操作队列前先加锁
        queue.push(1);
        if(queue.size() > 0)
        {
            LOGD("生产者通知消费者有产品产生,产品数量为:%d", queue.size());
            pthread_cond_signal(&cond); //有了产品通知消费者
        }
        pthread_mutex_unlock(&mutex); //解锁线程
        sleep(4); //休息4秒,单位是秒
    }
    pthread_exit(&pthread_produc);
}

void *customerThread(void *data)
{
    char *prod = (char *) data;
    LOGD("%", prod);
    while(1) //这里用死循环,时间情况应该给一个变量来控制跳出循环
    {
        pthread_mutex_lock(&mutex); //操作队列前先加锁
        if(queue.size() > 0)
        {
            queue.pop();
            LOGE("消费者消费一个产品,产品数量为:%d", queue.size());
        } else{
            LOGE("产品消费完了,等待生产者生产......");
            pthread_cond_wait(&cond, &mutex); //阻塞线程等待生产者的通知
        }
        pthread_mutex_unlock(&mutex);//解锁线程
        usleep(500 * 1000); //休息0.5秒,usleep单位是微妙
    }
    pthread_exit(&pthread_customer);
}

void initMutex()
{
    pthread_mutex_init(&mutex, NULL); //初始化锁对象 对应pthread_mutex_destroy销毁锁对象
    pthread_cond_init(&cond, NULL); //初始化条件变量 对应pthread_cond_destroy销毁条件变量
    pthread_create(&pthread_produc, NULL, ptoducThread, (void *) "product"); //创建生产者线程,并传递参数
    pthread_create(&pthread_customer, NULL, customerThread, NULL); //创建消费者线程
}

extern "C"
JNIEXPORT void JNICALL
Java_com_ywl5320_jnithread_JniThread_mutexThread(JNIEnv *env, jobject instance) {
    // TODO
    //初始化时,先往队列中添加10个产品
    for(int i = 0; i < 10; i++)
    {
        queue.push(i);
    }
    initMutex();
}

根据注释和上文的步骤思路就很好理解“生产者和消费者”模型了,在ffmpeg中音视频解码时就用到了这个模型,后面也会介绍用ffmpeg解码音视频的。现在是打基础的时候 哈哈。


源码下载:Github:Android-JniThread  欢迎star