NDK学习笔记(八) 线程相关

1.原生线程

(1) 原生代码使用java线程

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    EditText threads_edit;
    EditText iterations_edit;
    Button start_button;
    TextView log_view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        threads_edit = findViewById(R.id.threads_edit);
        iterations_edit = findViewById(R.id.iterations_edit);
        start_button = findViewById(R.id.start_button);
        log_view = findViewById(R.id.log_view);

        start_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                int threads = getNumber(threads_edit, 0);
                int iterations = getNumber(iterations_edit, 0);
                if (threads > 0 && iterations > 0) {

                    startThreads(threads, iterations);
                }
            }
        });
        nativeInit();
    }

    @Override
    protected void onDestroy() {
        nativeFree();
        super.onDestroy();
    }
	//从原生代码中调用,并传值
    public void onNativeMessage(final String message) {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                log_view.append(message);
                log_view.append("\n");
            }
        });
    }
	//获取数值
    private static int getNumber(EditText et, int def) {

        int value;
        try {
            value = Integer.parseInt(et.getText().toString());
        } catch (NumberFormatException e) {
            value = def;
        }
        return value;
    }

    private void startThreads(int thread, int iterations) {

        javaThreads(thread, iterations);
    }

	/**
	开启java线程
	*threads 线程数量
	*iterations  循环次数
	*/
    private void javaThreads(int threads, final int iterations) {


        for (int i = 0; i < threads; i++) {

            final int id = i;
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {

                    nativeWorker(id, iterations);
                }
            });
            thread.start();
        }

    }


    /**
     * 初始化原生代码
     */
    private native void nativeInit();

    /**
     * 释放原生资源
     */
    private native void nativeFree();

    /**
     * 原生worker
     */
    private native void nativeWorker(int id, int iterations);
}

原生代码

#include <jni.h>
#include <string>
#include <stdlib.h>
#include <unistd.h>


static jmethodID gOnNativeMessage = NULL;

extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeInit(JNIEnv *env, jobject thiz) {


    if (NULL == gOnNativeMessage) {
        jclass clazz = env->GetObjectClass(thiz);
        //获取onNativeMessage方法的id,以备调用
        gOnNativeMessage = env->GetMethodID(clazz, "onNativeMessage", "(Ljava/lang/String;)V");
        if (NULL == gOnNativeMessage) {

            jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
            env->ThrowNew(exceptionClazz, "没有找到onNativeMessage方法");
        }
    }
}



extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeFree(JNIEnv *env, jobject thiz) {
    // TODO: implement nativeFree()
}



extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeWorker(JNIEnv *env, jobject thiz, jint id,
                                                  jint iterations) {

    for (jint i = 0; i < iterations; i++) {
        char message[26];
        sprintf(message, "Worker %d: Iteration %d", id, i);

        jstring messageString = env->NewStringUTF(message);
        //调用java的onNativeMessage
        env->CallVoidMethod(thiz, gOnNativeMessage, messageString);

        if (NULL != env->ExceptionOccurred()) {

            break;
        }
        sleep(1);
    }
}

在这里插入图片描述

(2) POSIX线程(android)

在原生代码中使用POSIX线程需要的头文件

#include <pthread.h>
int pthread_create(pthread_t* __pthread_ptr, pthread_attr_t const* __attr, void* (*__start_routine)(void*), void*);

指向pthread_t类型变量的指针,函数用该指针返回新线程的句柄。
指向pthread_attr_t 结构的指针形式存在的新现场属性,可以通过该属性指定新线程的栈基址、栈大小、守护大小、调度策略和调度优先级等。
指向线程启动程序的函数指针,启动程序函数签名格式如下:

void* start_rountine(void* args)

实例:
与上面代码有公用部分。
新增native方法posixThreads(),用来创建新线程。
添加NativeWorkerArgs结构体,全局变量JavaVM、jobject,实现nativeFree方法。
新增JNI_OnLoad方法,此方法当共享库开始加载虚拟机自动调用该函数。
新增nativeWorkThread方法,将posix线程正确的附加到java虚拟机上,并将数据传给java。

具体的变动看代码:
java

package com.example.testnt;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    EditText threads_edit;
    EditText iterations_edit;
    Button start_button;
    TextView log_view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        threads_edit = findViewById(R.id.threads_edit);
        iterations_edit = findViewById(R.id.iterations_edit);
        start_button = findViewById(R.id.start_button);
        log_view = findViewById(R.id.log_view);

        start_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                int threads = getNumber(threads_edit, 0);
                int iterations = getNumber(iterations_edit, 0);
                if (threads > 0 && iterations > 0) {

                    //startThreads(threads, iterations);
                    posixThreads(threads, iterations);
                }
            }
        });

        nativeInit();

    }

    @Override
    protected void onDestroy() {
        nativeFree();
        super.onDestroy();
    }

    public void onNativeMessage(final String message) {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                log_view.append(message);
                log_view.append("\n");
            }
        });
    }

    private static int getNumber(EditText et, int def) {

        int value;
        try {
            value = Integer.parseInt(et.getText().toString());
        } catch (NumberFormatException e) {
            value = def;
        }
        return value;
    }

    private void startThreads(int thread, int iterations) {

        javaThreads(thread, iterations);
    }

    private void javaThreads(int threads, final int iterations) {


        for (int i = 0; i < threads; i++) {

            final int id = i;
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {

                    nativeWorker(id, iterations);
                }
            });
            thread.start();
        }

    }


    /**
     * 初始化原生代码
     */
    private native void nativeInit();

    /**
     * 释放原生资源
     */
    private native void nativeFree();

    /**
     * 原生worker
     */
    private native void nativeWorker(int id, int iterations);

    /**
     * 使用POSIX线程
     *
     * @param threads
     * @param iterations
     */
    private native void posixThreads(int threads, int iterations);
}

原生代码

#include <jni.h>
#include <string>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeWorker(JNIEnv *env, jobject thiz, jint id,
                                                  jint iterations);

static void *nativeWorkThread(void *args);


//定义结构体
struct NativeWorkerArgs {
    jint id;
    jint iterations;
};
//JAVa虚拟机接口指针
static JavaVM *gVm = NULL;
//对象的全局引用
static jobject gObj = NULL;

static jmethodID gOnNativeMessage = NULL;

extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeInit(JNIEnv *env, jobject thiz) {

    //为对象创建一个新的全局引用
    if (NULL == gObj) {
        gObj = env->NewGlobalRef(thiz);
    }
    if (NULL == gObj) {
        //结束此方法
        goto exit;
    }

    if (NULL == gOnNativeMessage) {
        jclass clazz = env->GetObjectClass(thiz);
        //获取onNativeMessage方法的id,以备调用
        gOnNativeMessage = env->GetMethodID(clazz, "onNativeMessage", "(Ljava/lang/String;)V");
        if (NULL == gOnNativeMessage) {

            jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
            env->ThrowNew(exceptionClazz, "没有找到onNativeMessage方法");
        }
    }

    exit:
    return;
}

/**
 * 当共享库开始加载虚拟机自动调用该函数,可以获取到JavaVM的指针
 * @param vm
 * @param reserved
 * @return
 */
jint JNI_OnLoad(JavaVM *vm, void *reserved) {

    gVm = vm;
    return JNI_VERSION_1_4;
}

//将posix线程正确的附着到java虚拟机上
static void *nativeWorkThread(void *args) {

    JNIEnv *env = NULL;
    //将当前线程附加到java虚拟机上
    if (0 == gVm->AttachCurrentThread(&env, NULL)) {

        //获取原生worker线程参数
        NativeWorkerArgs *nativeWorkerArgs = (NativeWorkerArgs *) args;
        //运行原生worker
        Java_com_example_testnt_MainActivity_nativeWorker(env, gObj, nativeWorkerArgs->id,
                                                          nativeWorkerArgs->iterations);
        delete nativeWorkerArgs;
        gVm->DetachCurrentThread();

    }

    return (void *) 1;
}


extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeFree(JNIEnv *env, jobject thiz) {

    if (NULL != gObj) {

        env->DeleteGlobalRef(gObj);
        gObj = NULL;
    }
}



extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeWorker(JNIEnv *env, jobject thiz, jint id,
                                                  jint iterations) {

    for (jint i = 0; i < iterations; i++) {
        char message[26];
        sprintf(message, "Worker %d: Iteration %d", id, i);

        jstring messageString = env->NewStringUTF(message);
        //调用java的onNativeMessage
        env->CallVoidMethod(thiz, gOnNativeMessage, messageString);

        if (NULL != env->ExceptionOccurred()) {

            break;
        }
        sleep(1);
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_posixThreads(JNIEnv *env, jobject thiz, jint threads,
                                                  jint iterations) {

    for (jint i = 0; i < threads; i++) {

        NativeWorkerArgs *nativeWorkerArgs = new NativeWorkerArgs();
        nativeWorkerArgs->id = i;
        nativeWorkerArgs->iterations = iterations;

        pthread_t thread;
        //创建一个新线程
        int result = pthread_create(&thread, NULL, nativeWorkThread, (void *) nativeWorkerArgs);
        if (0 != result) {

            jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
            env->ThrowNew(exceptionClazz, "创建线程失败");
        }
    }

}

为什么要附着在java虚拟机上。由于posix线程不是java平台的一部分,因此虚拟机不能识别他们。为了和java控件交互,posix线程应该先将自己附着到虚拟机上。

(3) 从POSIX线程返回结果

线程终止时,返回一个结果。通过线程启动程序返回的空指针实现的。
将函数修改成等待线程结束后再返回。pthread_join函数可以使一个函数等待线程终止。
紧接上面的代码,做出修改:

extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_posixThreads(JNIEnv *env, jobject thiz, jint threads,
                                                  jint iterations) {
    //线程句柄
    pthread_t *handles = new pthread_t[threads];

    for (jint i = 0; i < threads; i++) {

        NativeWorkerArgs *nativeWorkerArgs = new NativeWorkerArgs();
        nativeWorkerArgs->id = i;
        nativeWorkerArgs->iterations = iterations;

        //创建一个新线程
        int result = pthread_create(&handles[i], NULL, nativeWorkThread,
                                    (void *) nativeWorkerArgs);
        if (0 != result) {

            jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
            env->ThrowNew(exceptionClazz, "创建线程失败");
            goto exit;
        }
    }

    for (jint i = 0; i < threads; i++) {

        void *result = NULL;
        if (0 != pthread_join(handles[i], &result)) {

            jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
            env->ThrowNew(exceptionClazz, "join");
        } else {

            char message[26];
            sprintf(message, "Worker %d returned %d", i, result);
            jstring messageString = env->NewStringUTF(message);
            env->CallVoidMethod(thiz, gOnNativeMessage, messageString);
            if (NULL != env->ExceptionOccurred()) {

                goto exit;
            }
        }
    }

    exit:
        return;

}

运行后,点击按钮,会明显的感觉到卡了一会,这是由于pthread_join函数将UI的主线程挂起直到创建的线程终止。
效果:
在这里插入图片描述

(4) POSIX线程同步

常用同步机制:

互斥锁确保代码的互斥执行,即代码的特定不封不同时执行。

信号量控制对特定数目可用资源的访问,如果没有可用的资源,调用线程只是在信号量锁涉及的资源是哪个等待,直到资源可用。

①互斥锁

初始化
方式一

int pthread_mutex_init(pthread_mutex_t* __mutex, const pthread_mutexattr_t* __attr);

参数一:指向要初始化的互斥变量的指针
参数二:指向为互斥锁定义属性的pthread_mutexattr_t结构体指针,可设置成NULL,使用默认属性。

方式二

 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

锁定互斥锁

//如果互斥锁已经被锁上,调用线程被挂起直到互斥锁被打开。
//成功返回0,否则返回错误代码
int pthread_mutex_lock(pthread_mutex_t* __mutex);

解锁互斥锁

//如果成功,函数返回0,否则返回错误代码
int pthread_mutex_unlock(pthread_mutex_t* __mutex);

销毁互斥锁

int pthread_mutex_destroy(pthread_mutex_t* __mutex);

修改上面的代码

#include <jni.h>
#include <string>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeWorker(JNIEnv *env, jobject thiz, jint id,
                                                  jint iterations);

static void *nativeWorkThread(void *args);


//定义结构体
struct NativeWorkerArgs {
    jint id;
    jint iterations;
};
//JAVa虚拟机接口指针
static JavaVM *gVm = NULL;
//对象的全局引用
static jobject gObj = NULL;

static jmethodID gOnNativeMessage = NULL;

//互斥锁
static pthread_mutex_t mutex;

extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeInit(JNIEnv *env, jobject thiz) {

    //初始化互斥
    mutex = PTHREAD_MUTEX_INITIALIZER;
/*    if (0 != pthread_mutex_init(&mutex, NULL)) {
        jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
        env->ThrowNew(exceptionClazz, "没有初始化互斥锁");
        goto exit;
    }*/

    //为对象创建一个新的全局引用
    if (NULL == gObj) {
        gObj = env->NewGlobalRef(thiz);
    }
    if (NULL == gObj) {
        //结束此方法
        goto exit;
    }

    if (NULL == gOnNativeMessage) {
        jclass clazz = env->GetObjectClass(thiz);
        //获取onNativeMessage方法的id,以备调用
        gOnNativeMessage = env->GetMethodID(clazz, "onNativeMessage", "(Ljava/lang/String;)V");
        if (NULL == gOnNativeMessage) {

            jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
            env->ThrowNew(exceptionClazz, "没有找到onNativeMessage方法");
        }
    }

    exit:
    return;
}

/**
 * 当共享库开始加载虚拟机自动调用该函数,可以获取到JavaVM的指针
 * @param vm
 * @param reserved
 * @return
 */
jint JNI_OnLoad(JavaVM *vm, void *reserved) {

    gVm = vm;
    return JNI_VERSION_1_6;
}

//将posix线程正确的附着到java虚拟机上
static void *nativeWorkThread(void *args) {

    JNIEnv *env = NULL;
    //将当前线程附加到java虚拟机上
    if (0 == gVm->AttachCurrentThread(&env, NULL)) {

        //获取原生worker线程参数
        NativeWorkerArgs *nativeWorkerArgs = (NativeWorkerArgs *) args;
        //运行原生worker
        Java_com_example_testnt_MainActivity_nativeWorker(env, gObj, nativeWorkerArgs->id,
                                                          nativeWorkerArgs->iterations);
        delete nativeWorkerArgs;
        gVm->DetachCurrentThread();

    }

    return (void *) 1;
}


extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeFree(JNIEnv *env, jobject thiz) {

    if (NULL != gObj) {

        env->DeleteGlobalRef(gObj);
        gObj = NULL;
    }
    //销毁互斥锁
    if (0 != pthread_mutex_destroy(&mutex)) {

        jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
        env->ThrowNew(exceptionClazz, "不能都销毁互斥锁");
    }
}



extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_nativeWorker(JNIEnv *env, jobject thiz, jint id,
                                                  jint iterations) {
    //锁定互斥锁
    if (0 != pthread_mutex_lock(&mutex)) {

        jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
        env->ThrowNew(exceptionClazz, "不能锁定");
        goto exit;
    }

    for (jint i = 0; i < iterations; i++) {
        char message[26];
        sprintf(message, "Worker %d: Iteration %d", id, i);

        jstring messageString = env->NewStringUTF(message);
        //调用java的onNativeMessage
        env->CallVoidMethod(thiz, gOnNativeMessage, messageString);

        if (NULL != env->ExceptionOccurred()) {

            break;
        }
        sleep(1);
    }

    //解锁互斥锁
    if (0 != pthread_mutex_unlock(&mutex)) {

        jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
        env->ThrowNew(exceptionClazz, "不能解锁");
    }
    exit:
    return;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_testnt_MainActivity_posixThreads(JNIEnv *env, jobject thiz, jint threads,
                                                  jint iterations) {
    //线程句柄
    pthread_t *handles = new pthread_t[threads];

    for (jint i = 0; i < threads; i++) {

        NativeWorkerArgs *nativeWorkerArgs = new NativeWorkerArgs();
        nativeWorkerArgs->id = i;
        nativeWorkerArgs->iterations = iterations;

        //创建一个新线程
        int result = pthread_create(&handles[i], NULL, nativeWorkThread,
                                    (void *) nativeWorkerArgs);
        if (0 != result) {

            jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
            env->ThrowNew(exceptionClazz, "创建线程失败");
        }
    }
}

效果
在这里插入图片描述
可以明显的看到线程1执行完,线程0才能执行。

②使用信号量同步

需要使用头文件

#include <semaphore.h>

初始化信号量

信号量指针、共享标志、初始值
int sem_init(sem_t* __sem, int __shared, unsigned int __value);

锁定信号量

如果信号量的值大于零,上锁成功,并且信号量的值也会相应递减。
如果信号量的值是零,调用线程被挂起,直到另一个线程通过解锁它增加了信号量的值。
成功0  失败-1
int sem_wait(sem_t* __sem);

解锁信号量

当使用sem_post函数解锁信号量以后,信号量的值会增加1.
成功0  失败-1
int sem_post(sem_t* __sem);

销毁信号量

销毁一个线程正在阻塞的信号量有可能导致未知问题。
int sem_destroy(sem_t* __sem);

(5) POSIX线程调度策略

  • SCHED_FIFO:先进先出调度策略基于线程进入列表的时间对线程进行排序,也可以基于优先级在列表中移动线程。
  • SCHED_RR:循环轮转调度策略是线程执行时间加以限制的SCHED_FIFO,起目的是避免线程独占可用的CPU时间。

调度策略在头文件中

#include <sched.h>

如何使用?

  • 用pthread_create函数创建一个新线程时,用线程属性结构pthread_attr_t的sched_policy域来定义调度策略。
  • 在运行时用pthread_setschedparam函数定义调度策略
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值