作为多任务实现的一种机制,多线程应用得非常广泛,相对于多进程,多线程不仅运行效率高,而且还可以提高系统资源的使用效率。Android JNI多线程实际也是linux的多线程
多线程编程常用函数
1.线程创建
进程被创建时,系统会为其创建一个主线程,而要在进程中创建新的线程,则可以调用pthread_create:
pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(start_routine)(void*), void *arg);
start_routine为新线程的入口函数,arg为传递给start_routine的参数。
每个线程都有自己的线程ID,以便在进程内区分。线程ID在pthread_create调用时回返给创建线程的调用者;一个线程也可以在创建后使用pthread_self()调用获取自己的线程ID:
pthread_self (void) ;
2.线程退出
线程的退出方式有三:
(1)执行完成后隐式退出;
(2)由线程本身显示调用pthread_exit 函数退出;
pthread_exit (void * retval) ;
(3)被其他线程用pthread_cance函数终止:(android不支持)
pthread_cance (pthread_t thread) ;
在某线程中调用此函数,可以终止由参数thread 指定的线程。
如果一个线程要等待另一个线程的终止,可以使用pthread_join函数,该函数的作用是调用pthread_join的线程将被挂起直到线程ID为参数thread的线程终止:
pthread_join (pthread_t thread, void** threadreturn);
3.线程通信
线程互斥
互斥意味着“排它”,即两个线程不能同时进入被互斥保护的代码。Linux下可以通过pthread_mutex_t 定义互斥体机制完成多线程的互斥操作,该机制的作用是对某个需要互斥的部分,在进入时先得到互斥体,如果没有得到互斥体,表明互斥部分被其它线程拥有,此时欲获取互斥体的线程阻塞,直到拥有该互斥体的线程完成互斥部分的操作为止。
下面的代码实现了对共享全局变量x 用互斥体mutex 进行保护的目的:
int x; // 进程中的全局变量
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL); //按缺省的属性初始化互斥体变量mutex
pthread_mutex_lock(&mutex); // 给互斥体变量加锁
… //对变量x 的操作
phtread_mutex_unlock(&mutex); // 给互斥体变量解除锁
线程同步
同步就是线程等待某个事件的发生。只有当等待的事件发生线程才继续执行,否则线程挂起并放弃处理器。当多个线程协作时,相互作用的任务必须在一定的条件下同步。
Linux下的C语言编程有多种线程同步机制,最典型的是条件变量(condition variable)。pthread_cond_init用来创建一个条件变量,其函数原型为:
pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);
pthread_cond_wait和pthread_cond_timedwait用来等待条件变量被设置,值得注意的是这两个等待调用需要一个已经上锁的互斥体mutex,这是为了防止在真正进入等待状态之前别的线程有可能设置该条件变量而产生竞争。pthread_cond_wait的函数原型为:
pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
pthread_cond_broadcast用于设置条件变量,即使得事件发生,这样等待该事件的线程将不再阻塞:
pthread_cond_broadcast (pthread_cond_t *cond) ;
pthread_cond_signal则用于解除某一个等待线程的阻塞状态pthread_cond_signal (pthread_cond_t *cond) ;
pthread_cond_destroy 则用于释放一个条件变量的资源。
在头文件semaphore.h 中定义的信号量则完成了互斥体和条件变量的封装,按照多线程程序设计中访问控制机制,控制对资源的同步访问,提供程序设计人员更方便的调用接口。
sem_init(sem_t *sem, int pshared, unsigned int val);
这个函数初始化一个信号量sem 的值为val,参数pshared 是共享属性控制,表明是否在进程间共享。
sem_wait(sem_t *sem);
调用该函数时,若sem为无状态,调用线程阻塞,等待信号量sem值增加(post )成为有信号状态;若sem为有状态,调用线程顺序执行,但信号量的值减一。
sem_post(sem_t *sem);
eg.
创建Android.mk
OCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JNIThreads
LOCAL_SRC_FILES := JNI_Threads.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
创建JNI_Threads.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#include<jni.h>
#include<android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-activity", __VA_ARGS__))
#define NUMTHREADS 5
JavaVM *g_jvm = NULL;
jobject g_obj = NULL;
//初始化互斥锁
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
//初始化条件变量
pthread_cond_t condition_var = PTHREAD_COND_INITIALIZER;
//初始化计数变量
int num = 1;
sem_t sem;
void *thread_fun(void* arg)
{
JNIEnv *env;
jclass cls;
jmethodID mid;
//Attach主线程
if((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK)
{
LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
return NULL;
}
//找到对应的类
cls = (*env)->GetObjectClass(env,g_obj);
if(cls == NULL)
{
LOGE("FindClass() Error.....");
goto error;
}
//再获得类中的方法
mid = (*env)->GetStaticMethodID(env, cls, "fromJNI", "(I)V");
if (mid == NULL)
{
LOGE("GetMethodID() Error.....");
goto error;
}
//最后调用java中的静态方法
(*env)->CallStaticVoidMethod(env, cls, mid ,(int)arg);
error:
//Detach主线程
if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
{
LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
}
pthread_exit(0);
}
void *thread_fun1(void* arg)
{
<span style="white-space:pre"> </span>char *name = (char *)arg;
<span style="white-space:pre"> </span>LOGE("thread1 create: %s\n",name);
<span style="white-space:pre"> </span>while(1)
{
//上锁
pthread_mutex_lock( &count_mutex );
//如果num是奇数
if(num%2)
{
//输出当前num的值
LOGE("thread1 create: %d\n",num);
num++;
}
else
//解锁,等待
pthread_cond_wait( &condition_var, &count_mutex );
//解锁
pthread_mutex_unlock( &count_mutex );
if(num > 10)
return NULL;
}
}
void *thread_fun2(void* arg)
{
<span style="white-space:pre"> </span>char *name = (char *)arg;
<span style="white-space:pre"> </span>LOGE("thread2 create: %s\n",name);
<span style="white-space:pre"> </span>while(1)
{
//上锁
pthread_mutex_lock( &count_mutex );
//如果num是偶数
if(!(num%2))
{
//输出当前num的值
LOGE("thread2 create: %d\n",num);
num++;
}
else
//唤醒正在等待的线程
pthread_cond_signal( &condition_var );
//解锁
pthread_mutex_unlock( &count_mutex );
if(num > 10)
{
//最后再唤醒一次以防止死锁
pthread_cond_signal( &condition_var );
return NULL;
}
}
}
void *sem_threadFunc(void* arg)
{
<span style="white-space:pre"> </span>LOGE("wait sem post(sem will be posted after 3 seconds)");
<span style="white-space:pre"> </span>sem_wait(&sem);
<span style="white-space:pre"> </span>LOGE("sem posted");
}
JNIEXPORT void Java_com_sailor_thread_semThread(JNIEnv *env, jobject obj)
{
<span style="white-space:pre"> </span>int res;
<span style="white-space:pre"> </span>pthread_t thread;
<span style="white-space:pre"> </span>// pshared:控制信号量的类型,0表示这个信号量是当前进程的局部信号量,
<span style="white-space:pre"> </span>// 否则,这个信号量就可以在多个进程之间共享。
<span style="white-space:pre"> </span>// value:信号量的初始值。
<span style="white-space:pre"> </span>res = sem_init(&sem, 0, 0);
<span style="white-space:pre"> </span>if(res != 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("sem init failed");
<span style="white-space:pre"> </span>return;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>res = pthread_create(&thread, NULL, sem_threadFunc, NULL);
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>if(res != 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("create thread failed");
<span style="white-space:pre"> </span>return;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>LOGE("sem post");
<span style="white-space:pre"> </span>sleep(10);
<span style="white-space:pre"> </span>res = sem_post(&sem);
<span style="white-space:pre"> </span>if(res != 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("sem post failed");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>res = pthread_join(thread, NULL);
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>if(res != 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("pthread_join work1 failed");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("pthread_join work1 success");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>sem_destroy(&sem);
<span style="white-space:pre"> </span>
}
JNIEXPORT void Java_com_sailor_thread_workThread( JNIEnv* env, jobject obj)
{
<span style="white-space:pre"> </span>pthread_t t1,t2;
<span style="white-space:pre"> </span>int ret;
<span style="white-space:pre"> </span>ret = pthread_create(&t1, NULL, &thread_fun1, (void *)"work1");
<span style="white-space:pre"> </span>if(ret != 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("create thread work1 failed");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>ret = pthread_create(&t2, NULL, &thread_fun2, (void *)"work2");
<span style="white-space:pre"> </span>if(ret != 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("create thread work2 failed");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>//等待线程1结束
<span style="white-space:pre"> </span>ret = pthread_join(t1,NULL);
<span style="white-space:pre"> </span>if(ret != 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("pthread_join work1 failed");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>else
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("pthread_join work1 success");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>ret = pthread_join(t2,NULL);
<span style="white-space:pre"> </span>if(ret != 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LOGE("pthread_join work2 failed");
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>else{
<span style="white-space:pre"> </span>LOGE("pthread_join work2 success");
<span style="white-space:pre"> </span>}
}
//由java调用以创建子线程
JNIEXPORT void Java_com_sailor_thread_mainThread( JNIEnv* env, jobject obj)
{
int i;
pthread_t pt[NUMTHREADS];
for (i = 0; i < NUMTHREADS; i++){
//创建子线程
<span style="white-space:pre"> </span>LOGE("create thread.....%d", i);
pthread_create(&pt[i], NULL, &thread_fun, (void *)i);
}
}
//由java调用来建立JNI环境
JNIEXPORT void Java_com_sailor_thread_setJNIEnv( JNIEnv* env, jobject obj)
{
//保存全局JVM以便在子线程中使用
(*env)->GetJavaVM(env,&g_jvm);
//不能直接赋值(g_obj = obj)
g_obj = (*env)->NewGlobalRef(env,obj);
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env = NULL;
jint result = -1;
//获取JNI版本
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
{
LOGE("GetEnv failed!");
return result;
}
return JNI_VERSION_1_4;
}
Java层创建com.sailor.thread类
package com.sailor;
import android.util.Log;
public class thread {
static {
// 加载动态库
System.loadLibrary("JNIThreads");
}
public native void setJNIEnv();
public native void mainThread();
public native void workThread();
<span style="white-space:pre"> </span>public native void semThread();
public static void fromJNI(int i){
Log.v("Java------>", ""+i);
}
}
调用
public void startThread(View view){
final thread th = new thread();
th.setJNIEnv();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Log.d("jacklam", "start thread loop");
th.workThread();
Log.d("jacklam", "end thread loop");
Log.d("jacklam", "start sem thread loop");
th.semThread();
Log.d("jacklam", "end sem thread loop");</span>
}
});
thread.start();
th.mainThread();
}
代码地址:http://download.youkuaiyun.com/detail/kanwah200/7986007
附:
事项 | WIN32 | VxWorks | Linux |
线程创建 | CreateThread | taskSpawn | pthread_create |
线程终止 | 执行完成后退出;线程自身调用ExitThread函数即终止自己;被其他线程调用函数TerminateThread函数 | 执行完成后退出;由线程本身调用exit退出;被其他线程调用函数taskDelete终止 | 执行完成后退出;由线程本身调用pthread_exit 退出;被其他线程调用函数pthread_cance终止 |
获取线程ID | GetCurrentThreadId | taskIdSelf | pthread_self |
创建互斥 | CreateMutex | semMCreate | pthread_mutex_init |
获取互斥 | WaitForSingleObject、WaitForMultipleObjects | semTake | pthread_mutex_lock |
释放互斥 | ReleaseMutex | semGive | phtread_mutex_unlock |
创建信号量 | CreateSemaphore | semBCreate、semCCreate | sem_init |
等待信号量 | WaitForSingleObject | semTake | sem_wait |
释放信号量 | ReleaseSemaphore | semGive | sem_post |