JNI作為連接Java 和 本地C資源的一個(gè)非常重要的技術(shù), 需要被好好重視并掌握, 本章將總結一下JNI涉及的多線(xiàn)程問(wèn)題, 在此之前, 需要再次重申, JNI技術(shù)的應用背景:
1. 永遠只考慮Java對C代碼的調用, Java的優(yōu)勢在于GUI層面, C的優(yōu)勢在于執行效率. Java 涉及的是業(yè)務(wù)層入口, C考慮的是底層邏輯的執行。兩個(gè)簡(jiǎn)單的例子
a.> java技術(shù)本身涉及了眾多JNI調用, 如AWT/Swing這類(lèi)繪圖,最終是通過(guò)JNI調用的, 但Java面向程序員的API, 不會(huì )出現C以及接近底層的Natvie方法
b.> Android 技術(shù), 也只可能是Java調用C, 不會(huì )反過(guò)來(lái), 因為反過(guò)來(lái), 跟JNI設計的初衷不符合, 把問(wèn)題復雜化了.
2. 如果JNI將架構復雜化了, 說(shuō)明技術(shù)上不應該考慮JNI,或者你理解錯了.
3. 多考慮文件的接口, 流的接口, 而不是僅僅是對象的接口, 這樣會(huì )讓JNI的設計更加優(yōu)雅.
現在是JNI多線(xiàn)程的正題, 一個(gè)JNI多線(xiàn)程的例子程序, 設計一個(gè)多線(xiàn)程, 并且實(shí)現同步, 我理解的多線(xiàn)程需求如下:
1. 線(xiàn)程在Java端啟動(dòng), 兩個(gè)線(xiàn)程都調用C的方法
2. 有一個(gè)共同的數據, 被C的代碼修改, 要求線(xiàn)程能對這個(gè)修改做同步, 即線(xiàn)程1和線(xiàn)程2不能沖突
可能會(huì )有人想: 干嘛不在C這端做多線(xiàn)程以及同步呢? 這個(gè)問(wèn)題跟"干嘛不是C調用Java"類(lèi)似, C實(shí)質(zhì)上只是一個(gè)高效的數據處理器, 把東西傳給它處理, 處理完之后, 交給Java就OK. C 中不做復雜的線(xiàn)程同步等操作, 所有的復雜的調度操作, 都應該是上層完成的, C僅僅是傻傻的執行者, 不是管理者?!徱粋€(gè)Java Coder的技術(shù)情節吧!
【后續可能要利用一些存在C多線(xiàn)程的代碼, 這個(gè)另當別論. 暫時(shí)忽略, 后面有時(shí)間再總結這類(lèi)問(wèn)題】
通過(guò)代碼的方式, 我們看看這些是怎么實(shí)現的:
1. C中存在一個(gè)全局變量value = 1000. 這個(gè)將被三個(gè)方法調用, 一個(gè)加一, 另一個(gè)減一. 另外一個(gè)查看全局變量的值. 這兩個(gè)方法提供給外部的Java調用
- package com.ostrichmyself.jni;
- public class HelloWorld {
- public native void addOne();
- public native void minusOne();
- public native int getGlobalValue();
- }
對應的C代碼為:
- #include"com_ostrichmyself_jni_HelloWorld.h"
- #include <stdio.h>
- int value = 1000;
- JNIEXPORT void JNICALL Java_com_ostrichmyself_jni_HelloWorld_addOne
- (JNIEnv *env, jobject obj)
- {
- value = value +1;
- }
- JNIEXPORT void JNICALL Java_com_ostrichmyself_jni_HelloWorld_minusOne
- (JNIEnv *env, jobject obj)
- {
- value = value -1;
- }
- JNIEXPORT jint JNICALL Java_com_ostrichmyself_jni_HelloWorld_getGlobalValue
- (JNIEnv *env, jobject obj)
- {
- return value;
- }
2. 組織多線(xiàn)程代碼, 讓他對C發(fā)起多個(gè)線(xiàn)程的調用, 并操控全局變量.
- package com.ostrichmyself.jni;
- public class MainCygwin {
-
-
-
- static{
- System.loadLibrary("hello");
- }
- public static void main(String[] args) {
- HelloWorld hl = new HelloWorld();
- Thread t1 = new Thread(new Add("thread add ", hl));
- Thread t2 = new Thread(new Minus("thread Minus ", hl));
- t1.start();
- t2.start();
- }
-
- }
- class Minus implements Runnable{
-
- private String threadName;
- private HelloWorld hl;
-
- public Minus(String name, HelloWorld hl)
- {
- this.threadName = name;
- this.hl = hl;
- }
-
- @Override
- public void run() {
- int i = 0;
- while(i < 100)
- {
- i++;
- operation();
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- public void operation()
- {
- synchronized(hl)
- {
-
- System.out.println("begin:" + threadName + hl.getGlobalValue());
- hl.minusOne();
- try {
-
-
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- hl.minusOne();
- System.out.println("end:" + threadName + hl.getGlobalValue());
- }
- }
- }
- class Add implements Runnable{
-
- private String threadName;
- private HelloWorld hl;
-
- public Add(String name, HelloWorld hl)
- {
- this.threadName = name;
- this.hl = hl;
- }
-
- @Override
- public void run() {
- int i = 0;
- while(i < 10)
- {
- i++;
- operation();
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- public void operation()
- {
- synchronized(hl)
- {
-
- System.out.println("begin:" + threadName + hl.getGlobalValue());
- hl.addOne();
- System.out.println("end:" + threadName + hl.getGlobalValue());
- }
- }
- }
同步的變量是hl對象, 可以試一下, 注釋掉synchronized(hl) 打印的加法操作和減法操作會(huì )出現交叉混亂。
當然, 全局變量可以通過(guò)Java端傳入, 這樣更接近現實(shí)中的例子, 不過(guò)這個(gè)例程已經(jīng)有足夠的代表性。
編譯環(huán)境:
C: cygwin下gcc編譯
Java: Eclipse, Windows
源碼地址:
http://download.csdn.net/source/2393959
原文鏈接:
http://blog.csdn.net/ostrichmyself/article/details/5623804(###)