文章目录
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函数定义调度策略