作者:左少华 博客:http://blog.csdn.net/shaohuazuo/article/details/43149193 转载请注明出处:http://blog.csdn.net/shaohuazuo
这个是百度百科里的一段话.
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。
一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,
只拥有一点儿在运行中必不可少的资源, 但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。
就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;
阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。
每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
在默认情况下,一个App内的各类别(Activity,BroadcastReceiver,Serverice等)都会在同一个进程中执行,
而且这个进程由主线程负责执行,在Android里.一个app也可以同时运行多个进程,可以在manifest.xml文件中进行声明.
再组件后面添加android:process="name" name是进程的名字.可以随意取名.
3.1 在android中为了保持良好的用户体验,Android主线程主用来和用户交互.并产生子线程完成一些幕后工作.
3.2 有些后台用户的处理,比如播放音乐,下载图片,等一些耗时的工作就不能由主线程来完成了.
3.3 解决方法由两个.一个是线程.一个是使用进程.在Android中.我们通常使用线程来解决这样的问题.
也可以使用进程来解决.但是创建一个进程需要的资源和线程比,就和前面说的一样,一个是重量级的,一个是LWP的.
3.4 选定Android多线程解决的问题的话.那么不可避免的就是线程安全.资源的竞争问题,Java层的解决方法
和C层的解决原理是一样的.尽量不要产生竞争问题.如果由,则需要加锁保护.比如,java中的lock,同步,
c中的信号量,互斥锁mutex和读写锁,spinlock等等.
3.5 JNI是如何处理线程资源冲突问题的.请看第二节.
在Android环境中.定义两个主要的结构体.JavaVM和JavaEvn,在Java环境中.每个进程里可以诞生许多VM实例.
每个实例会有一个JavaVM结构体实例和他对应.但是在Android环境中.每个进程只能诞生一个VM实例,
所以只有一个JavaVM结构体对实例. 通常在VM加载*.so程序库时,
会先调用JNI_OnLoad()函数,在JNI_OnLoad()函数中会将JavaVM指针对象保存到c层JNI的全局变量中.
JavaVM对象是所有线程共享的.
2.1 JNIEnv对象,当Java线程调用到C层的JNI函数的时候.一定会进入VM,VM会产生一个相应的JNIEnv对象.
这JNIEnv对象和线程是一一对应的关系.
2.2 在调用JNIEnv中的函数时.多个线程调用的JNIEnv中的本地函数都是独立的.
因为VM会为每个线程产生一个JNIEnv对象实体.
2.3 在调用函数的时候如果不方便传递JNIEnv对象,可以先获取JavaVM对象,再使用GetEnv()函数获取JNIEnv对象.
2.4 如果c/c++层自己创建的线程,就必须向VM替它诞生相应的JNIEnv对象.并且回传该对象的指针的值.
jclass,jmethodID和jfieldID三者都指针.通过这个三个指针,能获取到java中的属性和方法.
这个类被载入的时候,这些指针都是有效的.一直到这个类被卸载为止.
jmethodID和jfieldID可以直接存储在c获取C++的全局变量中.它不需要NewGlobalRef()函数进行转换 .
它们是线程和函数之间共享的对象.
但是jobject的所有子类,必须使用NewGlobalRef()方法转化才能生成一个全局对象的引用.
它们默认情况下时局部的.不能函数共享,和多线程共享.
另外还有两个对象, GetStringUTFChars()和GetBytesArrayElements()函数所回传的数据是不需要
使用NewGlobalRef()转换的.
在Android中不管是主线程还是子线程都能通过JNI标准调用c++或者C函数.
同样C函数同样也可以调用到Android主线程和其他线程中的函数.
1. ResultValue 存储计算的结果信息的类.
2. ActNative是用来执行计算动作的类.
3. CounterNative它讲ResultValue对象传递给C层,并获取返回的一个数.
4. JNI Module获取ConterNative传递的ResultValue对象之后.获取setValue()方法.
5. JNI Module获取CounterNative对象的number对象.
6. 并把这个值赋值给ResultValue对象.
MainActivity代码:
package com.zuoshaohua.threadtest; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener { private Button runbtn; private Button exitbtn; private TextView counterView; private CounterNative cn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); exitbtn = (Button) this.findViewById(R.id.exit); runbtn = (Button) this.findViewById(R.id.run); counterView = (TextView) this.findViewById(R.id.counterView); exitbtn.setOnClickListener(this); runbtn.setOnClickListener(this); cn = new CounterNativesub1(); //这new的是子类对象.子类会先调用父类的构造函数,再调用自己的构造函数. } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View arg0) { int id = arg0.getId(); switch (id) { case R.id.run: ActNative.nativeExec(); counterView.setText(cn.result.getResult() + ""); break; case R.id.exit: this.finish(); break; default: this.finish(); break; } } }
ActNative.java代码
package com.zuoshaohua.threadtest; public class ActNative { public static native void nativeExec(); }
package com.zuoshaohua.threadtest; public abstract class CounterNative { private int number =10 ; public ResultValue result; static{ System.loadLibrary("native"); } public CounterNative() { number = getNumber(); result = new ResultValue(); nativeSetup(result); } public abstract int getNumber(); private native void nativeSetup(Object obj); }
package com.zuoshaohua.threadtest; public class CounterNativesub1 extends CounterNative{ @Override public int getNumber() { // TODO Auto-generated method stub return 13; } }
package com.zuoshaohua.threadtest; import android.util.Log; public class ResultValue { private int result; private String mThreadName; public int getResult() { return result; } public void setResult(int result) { this.result = result; Thread.currentThread().setName( Thread.currentThread().getName()+"-SetValue"); mThreadName = Thread.currentThread().getName(); Log.d("ResultValue",mThreadName); } }
#include "act.h" #include "native.h" #include <android/log.h> #include <stdio.h> //该过程中需要使用到的方法. //1.GetFieldId() //2.GetMethodID() //3.GetIntField() //4.CallVoidMethod(); //我们整理一下调用这些方法需要的参数 //jclass (JNICALL *GetObjectClass) // (JNIEnv *env, jobject obj); //1. //jfieldID (JNICALL *GetFieldID) //(JNIEnv *env, jclass clazz, const char *name, const char *sig); //从GetFieldID方法中我们找到他需要jclass对象. //我们只有CouterNative对象的jobject对象. //那么我们可以使用GetObjectClass()方法来获取该对象. //第一要获取的GetFieldID参数搞定. //2. //jmethodID (JNICALL *GetMethodID) //(JNIEnv *env, jclass clazz, const char *name, const char *sig); //我们上层需要获取的的方法是setValue方法.这个方法的类是ResultValue. //那么我们可以使用resultThis方法获取Class这对象的jclass对象. //3.jint (JNICALL *GetIntField) //(JNIEnv *env, jobject obj, jfieldID fieldID); //因为jobject对象都是局部对象.如果需要保存到全局变量中. //需要使用NewGlobalRef函数进行转换. //所有在natvieSetup中需要将this转换成全局变量. //4.void (JNICALL *CallVoidMethod) //(JNIEnv *env, jobject obj, jmethodID methodID, ...); //resultThis这个对象也需要使用NewGlobalRef转换成全局的变量. jfieldID number_id; jmethodID setv_id; jobject counter, result; JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_CounterNative_nativeSetup (JNIEnv * env, jobject this, jobject resultThis) { //获取jfieldID和jmethodID需要jclass对象.获取这个两个jclass对象. __android_log_print(ANDROID_LOG_INFO,"zshh","aaaaaaaaa"); jclass cnclz = (*env)->GetObjectClass(env,this); jclass resclz = (*env)->GetObjectClass(env,resultThis); __android_log_print(ANDROID_LOG_INFO,"zshh","bbbbbbbb"); number_id = (*env)->GetFieldID(env, cnclz,"number","I"); __android_log_print(ANDROID_LOG_INFO,"zshh","ccccccccc"); setv_id = (*env)->GetMethodID(env,resclz,"setResult","(I)V"); __android_log_print(ANDROID_LOG_INFO,"zshh","ddddddddd"); //将对象转换成全局对象. counter = (*env)->NewGlobalRef(env,this); result = (*env)->NewGlobalRef(env,resultThis); __android_log_print(ANDROID_LOG_INFO,"zshh","eeeeeeeeeee"); } JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_ActNative_nativeExec (JNIEnv *env, jclass this) { //获取number的值,累加10次之后.调用java层的setValue()设置给value //并在界面上显示出来. int i = 0; int number = (*env)->GetIntField(env,counter,number_id); __android_log_print(ANDROID_LOG_INFO,"zshh","number = %d", number); for(i = 0 ; i<10; i++) { number += 10; } __android_log_print(ANDROID_LOG_INFO,"zshh","number = %d", number); //调用setValue进行赋值. (*env) -> CallVoidMethod(env,result,setv_id,number); //end }
# Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # SRC_PATH_ROOT:=$(LOCAL_PATH)/../../src LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := native LOCAL_SRC_FILES := native.c LOCAL_LDLIBS := -llog #LOCAL_SHARED_LIBRARIES :=libc include $(BUILD_SHARED_LIBRARY)
当Java层的主线程准备执nativeSetup函数时,VM就会产生一个JNIEnv机构体对象.这个对象专属主线程.
接着将这个对象传递给nativeSetup()函数的第一参数. 如果由其他的线程也一样.VM也会替其他线程产生对应的JNIEnv对象.
所以不同的线程进入JNI层c函数时,他们JNIEnv的参数是不同的, 但是同一个线程每次进入本地函数,
他们使用的JNIEnv是同一个.这样我们可以避免多线程之间的线程安全问题.而且可以达成跨函数的数据共享.
作者:左少华 博客:http://blog.csdn.net/shaohuazuo/article/details/43149193 转载请注明出处:http://blog.csdn.net/shaohuazuo
在上面我们都是Java的线程通过JNI调用的C.我们需要看看C中的线程如何进入Java层进行调用.
我们在C中生成一个线程.那么这个线程同样需要VM帮助我们去产生JNIEnv对象.
再Java线程进入C本地函数是,这个对象自动VM自动创建的.那么C本地线程进入Java
则需要向VM进行登记告示VM,VM会为这个C线程创建一个JNIEnv对象,之后这个线程就能进入Java层了.
Activity : 用来显示Android线程计算的数据,和人机交互的界面.
PrimeNative: 这个类是用来和C和java沟通的类.
C模块:诞生四个线程计算质数.计算完成之后由注册的到java虚拟机中的线程调用java层的方法将数据显示出来.
有了上面三个类.我们们看看整例子的流程.
第一步.我们再Activity中实例话一个PrimeNative,这个时候会调用static{}静态代码块中的代码.
就会去加载动态库,这个时候就会调用到C模块中JNI_OnLoad()函数,我们需要在这个代码中注册我们的线程到VM中.
第二步.当上面完成之后.Java会执行构造函数中的代码.我们在构造函数中使用调用nativeSetup()函数,主要是为了
C能获取jclass或者jmethodID,jfieldID对象,
第三步.从java传递过来的参数.计算该参数范围内有那些是质数.然后把这些质数从C中返回到Android,然后Android
在进行实现.
第四步.将一个质数返回到Android过程如下,每当线程计算完毕,并且当前计算的是一个质数的话.我们就调用java层的
函数将这个质数显示出来.
*上面是整个程序的框图,我们再看看c模块线程之间程序分析图.*
.c层代码内存线程分析图.
我们C程序中只有一个进程,负责计算质数.当计算的数是质数的话,就调用callBack返回该质数显示到UI中.
1.java层代码.
package com.zuoshaohua.threadtest; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener { private Button runbtn; private Button exitbtn; public static TextView counterView; private PrimeNative pn; public static MainActivity ref; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ref = this; setContentView(R.layout.activity_main); exitbtn = (Button) this.findViewById(R.id.exit); runbtn = (Button) this.findViewById(R.id.run); counterView = (TextView) this.findViewById(R.id.counterView); pn = new PrimeNative(10, 100); exitbtn.setOnClickListener(this); runbtn.setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View arg0) { int id = arg0.getId(); switch (id) { case R.id.run: ActNative.exec(); break; case R.id.exit: this.finish(); break; default: this.finish(); break; } } }
package com.zuoshaohua.threadtest; public class ActNative { public static native void exec(); }
package com.zuoshaohua.threadtest; import android.os.Handler; import android.os.Message; public class PrimeNative { private int start; private int end; private static Handler h; private static String thName; static { System.loadLibrary("prime"); } public PrimeNative(int start, int end) { this.start = start; this.end = end; if (start > 0 && start < end) { init(start, end); // 将需要计算的数据传递C层. } h = new Handler() { public void handleMessage(Message msg) { MainActivity.ref .setTitle("value = " + String.valueOf(msg.arg1)); MainActivity.ref.counterView.append(msg.arg1+", "); } }; } void callBack(int result) { Message m = h.obtainMessage(1, result, 3, null); thName = Thread.currentThread().getName(); h.sendMessage(m); } private native void init(int start, int end); }
2.JNI代码
/************************************************************************* > File Name: prime.c > Author: zuoshaohua > Mail: zshh0604@163.com > Created Time: Wed 28 Jan 2015 04:40:40 PM ************************************************************************/ #include"prime.h" #include"act.h" #include<android/log.h> #include<stdio.h> JavaVM * jvm; jmethodID callbackId; jobject obj; static int startNum; static int endNum; static const char *classPathPrime = "com/zuoshaohua/threadtest/PrimeNative"; static const char *classPathAct = "com/zuoshaohua/threadtest/ActNative"; static JNINativeMethod methodPrime[] = { {"init", "(II)V", (void *)Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup} }; static JNINativeMethod methodExec[] = { {"exec","()V", (void*) Java_com_zuoshaohua_threadtest_ActNative_nativeExec} }; static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods){ jclass clz = (*env)->FindClass(env,className); //通过完整的类路径得jclass结构体. (*env)->RegisterNatives(env, clz, gMethods, numMethods); return JNI_TRUE; } static int registerNatives(JNIEnv* env){ registerNativeMethods(env, classPathPrime, methodPrime, sizeof(methodPrime) / sizeof(methodPrime[0])); registerNativeMethods(env, classPathAct, methodExec, sizeof(methodExec) / sizeof(methodExec[0])); return JNI_TRUE; } jint JNI_OnLoad(JavaVM* vm, void* reserved){ JNIEnv *env; jvm = vm; if ((*jvm)->GetEnv(jvm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) //将JNI的版本设置为1.4,并通过jvm获取JNIEnv结构体 return -1; if (registerNatives(env) != JNI_TRUE) //注册我们的本地方法到VM中. return -1; return JNI_VERSION_1_4; } /* * Class: com_zuoshaohua_threadtest_PrimeNative * Method: nativeSetup * Signature: (II)V */ JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup (JNIEnv *env, jobject this, jint start, jint end) { //获取start,和end.保存到一个起来. __android_log_print(ANDROID_LOG_INFO, "PrimeNative", "start = %d, end =%d\n", start, end); startNum = start; endNum = end; //1. 获取jclass对象. jclass clz =(*env)->GetObjectClass(env, this); //2. 获取callBack方法. callbackId = (*env)->GetMethodID(env,clz,"callBack","(I)V"); //3. 将clz转化成全局变量. obj = (*env)->NewGlobalRef(env,this); } int isPrime(int num) { int i = 0; for(i = 2; i*i <= num; i++) //这里必须是 i*i<= num, 比如25,如果 i*i<25, { //那么这个数就会被判为奇数.以为,i=5的时候. i*i = <span style="font-family:Courier New;">25,退出循环,</span> if( num%i == 0 ){ return 0; } } return 1; } /* * Class: com_zuoshaohua_threadtest_ActNative * Method: nativeExec * Signature: ()V */ JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_ActNative_nativeExec (JNIEnv * env, jclass clz) { int i = 0 ; for(i =startNum; i<endNum; i++) { if(isPrime(i)) { __android_log_print(ANDROID_LOG_INFO, "ActNative", "prime = %d", i); (*env)->CallVoidMethod(env,obj,callbackId,i); //回调java层方法,将数据显示到Java页面. } } //我们调用java端代码将数据显示到Android界面上 //那么我们需要将nativeSetup方法的调用对象转化成全局的对象并获取callBack方法的ID. __android_log_print(ANDROID_LOG_INFO, "ActNative", "Java_com_zuoshaohua_threadtest_ActNative_nativeExec"); }
需要修改jni层的代码如下:
//arm-linux-androideabi-g++: error: pthread: No such file or directory
/************************************************************************* > File Name: prime.c > Author: zuoshaohua > Mail: zshh0604@163.com > Created Time: Wed 28 Jan 2015 04:40:40 PM ************************************************************************/ #include"prime.h" #include"act.h" #include<android/log.h> #include<stdio.h> #include<pthread.h> JavaVM * jvm; jmethodID callbackId; jobject obj; static pthread_t primeThread; static int startNum; static int endNum; static const char *classPathPrime = "com/zuoshaohua/threadtest/PrimeNative"; static const char *classPathAct = "com/zuoshaohua/threadtest/ActNative"; extern void* pthreadPirme( void*); static JNINativeMethod methodPrime[] = { {"init", "(II)V", (void *)Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup} }; static JNINativeMethod methodExec[] = { {"exec","()V", (void*) Java_com_zuoshaohua_threadtest_ActNative_nativeExec} }; static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods){ jclass clz = (*env)->FindClass(env,className); //通过完整的类路径得jclass结构体. (*env)->RegisterNatives(env, clz, gMethods, numMethods); return JNI_TRUE; } static int registerNatives(JNIEnv* env){ registerNativeMethods(env, classPathPrime, methodPrime, sizeof(methodPrime) / sizeof(methodPrime[0])); registerNativeMethods(env, classPathAct, methodExec, sizeof(methodExec) / sizeof(methodExec[0])); return JNI_TRUE; } jint JNI_OnLoad(JavaVM* vm, void* reserved){ JNIEnv *env; jvm = vm; if ((*jvm)->GetEnv(jvm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) //将JNI的版本设置为1.4,并通过jvm获取JNIEnv结构体 return -1; if (registerNatives(env) != JNI_TRUE) //注册我们的本地方法到VM中. return -1; return JNI_VERSION_1_4; } /* * Class: com_zuoshaohua_threadtest_PrimeNative * Method: nativeSetup * Signature: (II)V */ JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup (JNIEnv *env, jobject this, jint start, jint end) { //获取start,和end.保存到一个起来. __android_log_print(ANDROID_LOG_INFO, "PrimeNative", "start = %d, end =%d\n", start, end); startNum = start; endNum = end; //1. 获取jclass对象. jclass clz =(*env)->GetObjectClass(env, this); //2. 获取callBack方法. callbackId = (*env)->GetMethodID(env,clz,"callBack","(I)V"); //3. 将clz转化成全局变量. obj = (*env)->NewGlobalRef(env,this); } int isPrime(int num) { int i = 0; for(i = 2; i*i <= num; i++) //这里必须是 i*i<= num, 比如25,如果 i*i<25, { //那么这个数就会被判为奇数.以为,i=5的时候. i*i = 25, //但是它循环不会继续. if( num%i == 0 ){ return 0; } } return 1; } /* * Class: com_zuoshaohua_threadtest_ActNative * Method: nativeExec * Signature: ()V */ JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_ActNative_nativeExec (JNIEnv * env, jclass clz) { int i = 0 ; #if 0 for(i =startNum; i<endNum; i++) { if(isPrime(i)) { __android_log_print(ANDROID_LOG_INFO, "ActNative", "prime = %d", i); (*env)->CallVoidMethod(env,obj,callbackId,i); //回调java层方法,将数据显示到Java页面. } } #endif pthread_create(&primeThread,NULL,pthreadPirme,NULL); //我们调用java端代码将数据显示到Android界面上 //那么我们需要将nativeSetup方法的调用对象转化成全局的对象并获取callBack方法的ID. __android_log_print(ANDROID_LOG_INFO, "ActNative", "Java_com_zuoshaohua_threadtest_ActNative_nativeExec"); pthread_join(primeThread, NULL); <span style="font-family:Courier New;"> </span>//回收子进程. } #if 0 #include <pthread.h> //包含这个头文件. int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); Compile and link with -pthread. //编译是需要链接这个动态库. 如果您使用的是ubuntu系统,可以使用man pthread_create查看函数的说明. 参数一: pthread_t类型的指针,它是一个全局区域的一个变量. 该方法会填充这个结构体. 参数二: pthread_attr_t 它用来指定线程的属性. 参数三: 他是线程执行的函数. 参数四: 它是线程执行函数的参数. 1.先GetEnv方法获取这个env对象.如果已经存在,则不需要执行AttachCurrentThread()了.如果不存在,则需要注册. #endif void* pthreadPirme(void* args){ int i; int status; JNIEnv *env; jboolean isAttached = JNI_FALSE; status = (*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_4); if(status < 0) { status = (*jvm)->AttachCurrentThread(jvm,&env, NULL);//将当前线程注册到虚拟机中. if(status < 0) return NULL; isAttached = JNI_TRUE; } <span style="font-family:Courier New;"> </span> for(i =startNum; i<endNum; i++) { if(isPrime(i)) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "prime = %d", i); (*env)->CallVoidMethod(env,obj,callbackId,i);//回调java层方法,将数据显示到Java页面. } } if(isAttached) (*jvm)->DetachCurrentThread(jvm); //当前线程退出的话,讲线程从vm的注销. return NULL; }
/************************************************************************* > File Name: prime.c > Author: zuoshaohua > Mail: zshh0604@163.com > Created Time: Wed 28 Jan 2015 04:40:40 PM ************************************************************************/ #include"prime.h" #include"act.h" #include<android/log.h> #include<stdio.h> #include<pthread.h> #include <stdlib.h> #define PROC 4 JavaVM * jvm; //回调callBack函数的id. jmethodID callbackId; //这个对象是init函数传递下来的对象. //Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup //是从这个方法中得到的一个对象. jobject syncObj; //存储java传递过出来的开始num和结束num; static int startNum; static int endNum; //一定义一个结构体. //num: 需要判断num是一个质数还是一个偶数. //mutex: 他一个共享锁,用来避免线程之间的竞争问题. //cond: 它是一个条件变量. typedef struct item_t { int num; pthread_mutex_t mutex; pthread_cond_t cond; }Item_t; //主进程会把需要计算的数存放到item中. static Item_t * item; static int count[PROC]; //函数的定义 static void parent(void); static void * child(void *p); jboolean prime(int num); static int getNum(void); static void setNum(int i); static const char *classPathPrime = "com/zuoshaohua/threadtest/PrimeNative"; static const char *classPathAct = "com/zuoshaohua/threadtest/ActNative"; static JNINativeMethod methodPrime[] = { {"init", "(II)V", (void *)Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup} }; static JNINativeMethod methodExec[] = { {"exec","()V", (void*) Java_com_zuoshaohua_threadtest_ActNative_nativeExec} }; static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods){ jclass clz = (*env)->FindClass(env,className); //通过完整的类路径得jclass结构体. (*env)->RegisterNatives(env, clz, gMethods, numMethods); return JNI_TRUE; } static int registerNatives(JNIEnv* env){ registerNativeMethods(env, classPathPrime, methodPrime, sizeof(methodPrime) / sizeof(methodPrime[0])); registerNativeMethods(env, classPathAct, methodExec, sizeof(methodExec) / sizeof(methodExec[0])); return JNI_TRUE; } jint JNI_OnLoad(JavaVM* vm, void* reserved){ JNIEnv *env; jvm = vm; if ((*jvm)->GetEnv(jvm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) //将JNI的版本设置为1.4,并通过jvm获取JNIEnv结构体 return -1; if (registerNatives(env) != JNI_TRUE) //注册我们的本地方法到VM中. return -1; return JNI_VERSION_1_4; } /* * Class: com_zuoshaohua_threadtest_PrimeNative * Method: nativeSetup * Signature: (II)V */ JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup (JNIEnv *env, jobject this, jint start, jint end) { //获取start,和end.保存到一个起来. __android_log_print(ANDROID_LOG_INFO, "PrimeNative", "start = %d, end =%d\n", start, end); startNum = start; endNum = end; //1. 获取jclass对象. jclass clz =(*env)->GetObjectClass(env, this); //2. 获取callBack方法. callbackId = (*env)->GetMethodID(env,clz,"callBack","(I)V"); //3. 将clz转化成全局变量. syncObj = (*env)->NewGlobalRef(env,this); } /* * Class: com_zuoshaohua_threadtest_ActNative * Method: nativeExec * Signature: ()V */ JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_ActNative_nativeExec (JNIEnv * env, jclass clz) { int64_t i; int j, ret, flag = 0; pthread_t tid[PROC]; item = (Item_t*) malloc(sizeof(Item_t)); if (item == NULL) { exit(1); } item->num = 0; pthread_mutex_init(&item->mutex, NULL); pthread_cond_init(&item->cond, NULL); for (i=0;i<PROC;i++) { count[i] = 0; ret = pthread_create(tid+i, NULL, child, (void *)i); if (ret != 0) { exit(1); } } parent(); for(i = 0;i<PROC;i++) { pthread_join(tid[i],NULL); } pthread_mutex_destroy(&item->mutex); pthread_cond_destroy(&item->cond); free(item); } //判断一个数是步是质数.如果是质素返回JNI_TRUE =1 ,否则返回JNI_FALSE = 0 ; jboolean prime(int num) { int i = 0; for(i = 2;i * i <= num; i++) { if(num % i == 0) { return JNI_FALSE; } } return JNI_TRUE; } //下面我们写一个getNum()和setNum()的辅助方法. static int getNum(void) { int num; num = item->num; return num; } static void setNum(int i) { item->num = i; return; } //下面我们需要写一个子线程中需要处理的任务. //p:是传入线程再线程组中的下标,也就是 static void * child(void *p) { JNIEnv *env; int ret ,i; int64_t j; int status; jboolean isAttached = JNI_FALSE; status = (*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_4); if(status < 0) { status = (*jvm)->AttachCurrentThread(jvm,&env, NULL);//将当前线程注册到虚拟机中. if(status < 0) return NULL; isAttached = JNI_TRUE; } //我们需要由一个while的大循环, //它的退出条件是当主进程把数据分发完成之后. //主进程会把num设置为-1,并发送广播进行通知.让所有子进程退出. j = (int64_t)p; while(1) { //num是一个共享变量,所有获取前必须lock ret = pthread_mutex_lock(&item->mutex); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "pthread_muext_lock error\n"); exit(1); } while((i = getNum())==0) { //如果i==0,那么我们需要解锁等待.当条件满足的时候. //pthread_cond_wait()会进行加锁,他是一个原子操作. //这里为时候使用while而不使用if呢.是因为一次成功的加锁之后. //它调用getNum,不一定能获取需要计算的数.所有只能获取的数为0,那么需要进入睡眠等待. ret = pthread_cond_wait(&item->cond,&item->mutex); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "pthread_muext_lock error\n"); exit(1); } } if(i == -1) { //我们需要解锁退出. ret = pthread_mutex_unlock(&item->mutex); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "pthread_mutex_unlock(&item->mutex)\n"); exit(1); } break; } //如果不是上面的两种值.那么我们将num设置为0, //并发送广播通知正在等待的主进程,给num赋值. setNum(0); ret = pthread_cond_broadcast(&item->cond); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "pthread_cond_broadcast(&item->cond)"); exit(1); } ret = pthread_mutex_unlock(&item->mutex); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "pthread_mutex_unlock(&item->mutex)"); exit(1); } //如果是一个质数,那么他需要进入Java层,将这个质数显示的界面上. if(prime(i)) { //以为可能同时有多个线程进入java执行callBack函数.所以为了线程安全,我们必须调用MonitorEnter //相当于是对这个对象做加锁操作. (*env)->MonitorEnter(env,syncObj); (*env)->CallVoidMethod(env,syncObj,callbackId,i);//回调java层方法,将数据显示到Java页面. (*env)->MonitorEnter(env,syncObj); } count[j]++; } //打算该线程计算了多少个质数. __android_log_print(ANDROID_LOG_INFO,"child","pthreadPrime[%d]", count[j]); //任务完成退出当前线程. if(isAttached) (*jvm)->DetachCurrentThread(jvm); //再从线程退出之前.我们需要将线程从vm中注销. pthread_exit((void *)NULL); } //我们再分析一下父进程需要做的事情. //1.产生4个线程. //2.把需要计算质数方法num中.通知子线程获取数据进行计算. //3.当分发完这些数据之后,需要将num 设置为-1,并发送广播通知子线程退出. static void parent(void) { int i,ret,j; for(i=startNum;i<endNum;i++) { while(1) { ret = pthread_mutex_lock(&item->mutex); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "parent:: mutex_lock"); exit(1); } //如果大于则需要解锁等待.和上面的流程一样. while((j= getNum()) > 0) { ret = pthread_cond_wait(&item->cond,&item->mutex); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "parent:: cond_wait"); exit(0); } } if(j==0) { setNum(i); ret = pthread_cond_broadcast(&item->cond); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "parent:: brocast"); exit(0); } ret = pthread_mutex_unlock(&item->mutex); if(ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "parent:: mutex_unlock"); exit(0); } break; } } } while(1) { pthread_mutex_lock(&item->mutex); if (ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "parent:: mutex_lock"); exit(1); } while ((j = getNum()) > 0) { ret = pthread_cond_wait(&item->cond, &item->mutex); if (ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "parent::cond_wait"); exit(1); } } if (j == 0) { setNum(-1); ret = pthread_cond_broadcast(&item->cond); if (ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "parent:: broadcast"); exit(1); } ret = pthread_mutex_unlock(&item->mutex); if (ret != 0) { __android_log_print(ANDROID_LOG_INFO, "pthreadPrime", "parent:: mutex_unlock"); exit(1); } break; } } }
3.Android.mk文件
# Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # SRC_PATH_ROOT:=$(LOCAL_PATH)/../../src LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := prime LOCAL_SRC_FILES := prime.c LOCAL_LDLIBS := -llog LOCAL_SHARED_LIBRARIES := pthread include $(BUILD_SHARED_LIBRARY)
作者:左少华 博客:http://blog.csdn.net/shaohuazuo/article/details/43149193 转载请注明出处:http://blog.csdn.net/shaohuazuo
原文地址:http://blog.csdn.net/shaohuazuo/article/details/43149193