🔥 Android Studio 版本 🔥
🔥 了解线程同步的两个变量 🔥
pthread_mutex_t 互斥锁
线程的互斥: 目前存在两个线程 , 线程A和线程B, 只允许只有一个资源对临界资源进程操作 (大概意思就是 : A线程 进入操作临界资源的时候 , 那么 B线程 就要进行等待 . 等到 A线程 操作临界资源完成退出临界区后, 这时候 B线程 才能对临界区进行访问 .) . 每次保障只有线程访问临界资源 . 线程锁就是起到保护的作用 , 我们可以对某一个临界区进行加锁 . 在任意时间只有线程 执行临界区的代码. 这样就实现了多线程之间的互斥 .
pthread_mutex_t 条件变量
条件变量 : 是用来实现线程之间的 唤醒和释放 , 我们可以控制线程进行等待 , 线程等待之后可以通知线程结束等待 后继续执行 .
互斥锁使用不当就会造成线程之间的死锁 , 就会造成线程一直等待 .
🔥 创建JNI 🔥
package com.cmake.ndk1.jni; public class JNIWaitNotify { static { System.loadLibrary("wait-notify-lib"); } public native void waitNativeThread(); public native void notifyNativeThread(); }
🔥 添加动态库配置 🔥
add_library( #动态库名称 wait-notify-lib wait-notify-lib SHARED jni/jni_wait_notify.cpp )
🔥 生成可关联的库链接 🔥
为了让Java能够调用 wait-notify-lib 库中的函数,您需要使用 CMake 构建脚本中的 target_link_libraries() 命令来关联wait-notify-lib 库
target_link_libraries( # Specifies the target library. ndk1 person-lib dynamic-lib basic-type-lib string-lib reference-type-lib access-field-lib access-method-lib invoke-method-lib constructor-class-lib reference-lib exception-lib thread-lib wait-notify-lib # Links the target library to the log library # included in the NDK. ) )
🔥 Native层通过 waitNativeThread 实现线程阻塞 🔥
#include <jni.h> #include <pthread.h> #include <jvm.h> pthread_mutex_t mutex; pthread_cond_t cond; //创建两个线程的句柄 pthread_t waitHandle; pthread_t notifyHandle; //设置一个线程等待的标志 int flag=0; //创建线程运行的函数 void *waitThread(void *){ LOGI("wait thread lock"); //临界区进行加锁 pthread_mutex_lock(&mutex); //如果flag=0,就让线程一直等待 while (flag ==0 ) { LOGI("waiting"); //做一个线程等待操作 pthread_cond_wait(&cond, &mutex); } LOGI("wait thread unlock"); //临界区锁进行释放 pthread_mutex_unlock(&mutex); //加上线程退出的函数,显示退出 pthread_exit(0); }
extern "C" JNIEXPORT void JNICALL Java_com_cmake_ndk1_jni_JNIWaitNotify_waitNativeThread(JNIEnv *env, jobject thiz) { //初始化 pthread_mutex_init(&mutex, nullptr); pthread_cond_init(&cond, nullptr); pthread_create(&waitHandle, nullptr,waitThread, nullptr); }
运行结果
package com.cmake.ndk1; import android.os.Bundle; import android.view.View; import android.widget.TextView; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.cmake.ndk1.jni.JNIThread; import com.cmake.ndk1.jni.JNIWaitNotify; public class MainActivity13 extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final JNIWaitNotify jniWaitNotify = new JNIWaitNotify(); final TextView textView = findViewById(R.id.sample_text); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { jniWaitNotify.waitNativeThread(); //jniWaitNotify.notifyNativeThread(); } }); } }
I/LOG_JNI: wait thread lock I/LOG_JNI: waiting
🔥 Native层通过 notifyNativeThread 实现线程唤醒 🔥
#include <jni.h> #include <pthread.h> #include <jvm.h> pthread_mutex_t mutex; pthread_cond_t cond; //创建两个线程的句柄 pthread_t waitHandle; pthread_t notifyHandle; //设置一个线程等待的标志 int flag=0; //创建线程运行的函数 void *waitThread(void *){ LOGI("wait thread lock"); //临界区进行加锁 pthread_mutex_lock(&mutex); //如果flag=0,就让线程一直等待 while (flag ==0 ) { LOGI("waiting"); //做一个线程等待操作 pthread_cond_wait(&cond, &mutex); } LOGI("wait thread unlock"); //临界区锁进行释放 pthread_mutex_unlock(&mutex); //加上线程退出的函数,显示退出 pthread_exit(0); } //唤起线程中改变flag的值 void *notifyThread(void *){ LOGE("notify thread lock"); //临界区进行加锁 pthread_mutex_lock(&mutex); //改变flag的值等于1,唤起线程 释放锁 flag=1; //临界区锁进行释放 pthread_mutex_unlock(&mutex); //唤醒上一个阻塞的线程 pthread_cond_signal(&cond); LOGE("signal......"); LOGE("notify thread unlock"); //加上线程退出的函数,显示退出 pthread_exit(0); } extern "C" JNIEXPORT void JNICALL Java_com_cmake_ndk1_jni_JNIWaitNotify_waitNativeThread(JNIEnv *env, jobject thiz) { //初始化 pthread_mutex_init(&mutex, nullptr); pthread_cond_init(&cond, nullptr); pthread_create(&waitHandle, nullptr,waitThread, nullptr); } extern "C" JNIEXPORT void JNICALL Java_com_cmake_ndk1_jni_JNIWaitNotify_notifyNativeThread(JNIEnv *env, jobject thiz) { pthread_create(¬ifyHandle, nullptr,notifyThread, nullptr); }
运行结果 :
package com.cmake.ndk1; import android.os.Bundle; import android.view.View; import android.widget.TextView; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.cmake.ndk1.jni.JNIThread; import com.cmake.ndk1.jni.JNIWaitNotify; public class MainActivity13 extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final JNIWaitNotify jniWaitNotify = new JNIWaitNotify(); final TextView textView = findViewById(R.id.sample_text); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { jniWaitNotify.waitNativeThread(); jniWaitNotify.notifyNativeThread(); } }); } }
I/LOG_JNI: wait thread lock I/LOG_JNI: wait thread unlock E/LOG_JNI: notify thread lock E/LOG_JNI: signal...... E/LOG_JNI: notify thread unlock
🔥 线程阻塞唤醒代码流程梳理 🔥
//创建线程运行的函数 void *waitThread(void *){ LOGI("wait thread lock"); //临界区进行加锁 pthread_mutex_lock(&mutex); //如果flag=0,就让线程一直等待 while (flag ==0 ) { LOGI("waiting"); //做一个线程等待操作 pthread_cond_wait(&cond, &mutex); } LOGI("wait thread unlock"); //临界区锁进行释放 pthread_mutex_unlock(&mutex); //加上线程退出的函数,显示退出 pthread_exit(0); }
做一个线程等待操作 (pthread_cond_wait) 前面加一个临界锁 (pthread_mutex_lock) , 当其他函数想要持有这个临界锁是持有不到的 , 因为我们这里并没有执行释放锁 (pthread_mutex_unlock) .
当我们调用了线程等待操作 ( pthread_cond_wait ) , 就会将线程放到阻塞的线程队列中去 (即: 待唤醒的线程队列中去 ) 并释放锁 ,
//唤起线程中改变flag的值 void *notifyThread(void *){ LOGE("notify thread lock"); //临界区进行加锁 pthread_mutex_lock(&mutex); //改变flag的值等于1,唤起线程 释放锁 flag=1; //临界区锁进行释放 pthread_mutex_unlock(&mutex); //唤醒上一个阻塞的线程 pthread_cond_signal(&cond); LOGE("signal......"); LOGE("notify thread unlock"); //加上线程退出的函数,显示退出 pthread_exit(0); }
唤起线程里面的 临界区进行加锁 (pthread_mutext_lock) 其实用处不大 , 是因为这个锁已经被 waitThread 函数里面的 线程等待操作 (pthread_cond_wait) 持有锁了 , 只有 pthread_cond_wait 释放了锁, 那么 pthread_mutext_lock 才会拿到互斥锁 继续往下执行 , 指导释放锁 .
为什么加上锁 ( pthread_mutex_lock )和释放锁 (pthread_mutext_unlock) , 如果不加即便执行了唤醒线程提示(pthread_cond_signal) 执行了 , 那么线程等待操作 ( pthread_cond_wait ) 也不会执行 . 所以调用了 唤醒线程提示(pthread_cond_signal) 就会出现唤醒丢失的情况 .