分享

JNI 的多线程

 9loong 2012-10-09

JNI作为连接Java 和 本地C资源的一个非常重要的技术, 需要被好好重视并掌握, 本章将总结一下JNI涉及的多线程问题, 在此之前, 需要再次重申, JNI技术的应用背景:

1.  永远只考虑Java对C代码的调用, Java的优势在于GUI层面, C的优势在于执行效率. Java 涉及的是业务层入口, C考虑的是底层逻辑的执行。两个简单的例子

           a.> java技术本身涉及了众多JNI调用, 如AWT/Swing这类绘图,最终是通过JNI调用的, 但Java面向程序员的API, 不会出现C以及接近底层的Natvie方法

           b.> Android 技术, 也只可能是Java调用C, 不会反过来, 因为反过来, 跟JNI设计的初衷不符合, 把问题复杂化了.

 

2. 如果JNI将架构复杂化了, 说明技术上不应该考虑JNI,或者你理解错了.

3. 多考虑文件的接口, 流的接口, 而不是仅仅是对象的接口, 这样会让JNI的设计更加优雅.

 

 

现在是JNI多线程的正题, 一个JNI多线程的例子程序, 设计一个多线程, 并且实现同步, 我理解的多线程需求如下:

 

1. 线程在Java端启动, 两个线程都调用C的方法

 

2. 有一个共同的数据, 被C的代码修改, 要求线程能对这个修改做同步, 即线程1和线程2不能冲突

 

 

可能会有人想: 干嘛不在C这端做多线程以及同步呢? 这个问题跟"干嘛不是C调用Java"类似, C实质上只是一个高效的数据处理器, 把东西传给它处理, 处理完之后, 交给Java就OK. C 中不做复杂的线程同步等操作, 所有的复杂的调度操作, 都应该是上层完成的, C仅仅是傻傻的执行者, 不是管理者。——原谅一个Java Coder的技术情节吧!

【后续可能要利用一些存在C多线程的代码, 这个另当别论. 暂时忽略, 后面有时间再总结这类问题】

 

通过代码的方式, 我们看看这些是怎么实现的:

 

1. C中存在一个全局变量value = 1000. 这个将被三个方法调用, 一个加一, 另一个减一.  另外一个查看全局变量的值. 这两个方法提供给外部的Java调用

 

  1. package com.ostrichmyself.jni;  
  2. public class HelloWorld {  
  3.     public native void addOne();  
  4.     public native void minusOne();  
  5.     public native int getGlobalValue();  
  6. }  

对应的C代码为:

 

  1. #include"com_ostrichmyself_jni_HelloWorld.h"  
  2. #include <stdio.h>  
  3. int value = 1000;  
  4. JNIEXPORT void JNICALL Java_com_ostrichmyself_jni_HelloWorld_addOne  
  5.   (JNIEnv *env, jobject obj)  
  6.   {  
  7.     value = value +1;  
  8.   }  
  9. JNIEXPORT void JNICALL Java_com_ostrichmyself_jni_HelloWorld_minusOne  
  10.   (JNIEnv *env, jobject obj)  
  11.   {  
  12.     value = value -1;  
  13.   }  
  14. JNIEXPORT jint JNICALL Java_com_ostrichmyself_jni_HelloWorld_getGlobalValue  
  15.   (JNIEnv *env, jobject obj)  
  16.   {  
  17.     return value;  
  18.   }  

 

2. 组织多线程代码, 让他对C发起多个线程的调用, 并操控全局变量.

  1. package com.ostrichmyself.jni;  
  2. public class MainCygwin {  
  3.     /** 
  4.      * @param args 
  5.      */  
  6.     static{  
  7.         System.loadLibrary("hello");  
  8.     }  
  9.     public static void main(String[] args) {  
  10.         HelloWorld hl = new HelloWorld();  
  11.         Thread t1 = new Thread(new Add("thread add ", hl));  
  12.         Thread t2 = new Thread(new Minus("thread Minus ", hl));  
  13.         t1.start();  
  14.         t2.start();  
  15.     }  
  16.       
  17. }  
  18. class Minus implements Runnable{  
  19.       
  20.     private String threadName;  
  21.     private HelloWorld hl;  
  22.       
  23.     public Minus(String name, HelloWorld hl)  
  24.     {  
  25.         this.threadName = name;  
  26.         this.hl = hl;  
  27.     }  
  28.       
  29.     @Override  
  30.     public void run() {  
  31.         int i = 0;   
  32.         while(i < 100)  
  33.         {  
  34.             i++;  
  35.             operation();  
  36.             try {  
  37.                 Thread.sleep(10);  
  38.             } catch (InterruptedException e) {  
  39.                 e.printStackTrace();  
  40.             }  
  41.         }  
  42.     }  
  43.       
  44.     public void operation()  
  45.     {  
  46.         synchronized(hl)  
  47.         {  
  48.             //加上前后的两句打印, 是为了观察是否被其它线程入侵  
  49.             System.out.println("begin:" + threadName + hl.getGlobalValue());  
  50.             hl.minusOne();  
  51.             try {  
  52.                 //让线程进入休眠, 这个时候, 是带着锁的休眠  
  53.                 //如果其它线程不需要这把锁,这个时候将容易被其他线程入侵  
  54.                 Thread.sleep(100);  
  55.             } catch (InterruptedException e) {  
  56.                 e.printStackTrace();  
  57.             }  
  58.             hl.minusOne();  
  59.             System.out.println("end:" + threadName + hl.getGlobalValue());  
  60.         }  
  61.     }  
  62. }  
  63. class Add implements Runnable{  
  64.       
  65.     private String threadName;  
  66.     private HelloWorld hl;  
  67.       
  68.     public Add(String name, HelloWorld hl)  
  69.     {  
  70.         this.threadName = name;  
  71.         this.hl = hl;  
  72.     }  
  73.       
  74.     @Override  
  75.     public void run() {  
  76.         int i = 0;   
  77.         while(i < 10)  
  78.         {  
  79.             i++;  
  80.             operation();  
  81.             try {  
  82.                 Thread.sleep(10);  
  83.             } catch (InterruptedException e) {  
  84.                 e.printStackTrace();  
  85.             }  
  86.         }  
  87.     }  
  88.       
  89.     public void operation()  
  90.     {  
  91.         synchronized(hl)  
  92.         {  
  93.             //加上前后的两句打印, 是为了观察是否被其它线程入侵  
  94.             System.out.println("begin:" + threadName + hl.getGlobalValue());  
  95.             hl.addOne();  
  96.             System.out.println("end:" + threadName + hl.getGlobalValue());  
  97.         }  
  98.     }  
  99. }  

 

同步的变量是hl对象, 可以试一下, 注释掉synchronized(hl)  打印的加法操作和减法操作会出现交叉混乱。

 

当然, 全局变量可以通过Java端传入,  这样更接近现实中的例子, 不过这个例程已经有足够的代表性。

 

编译环境:

C: cygwin下gcc编译
Java: Eclipse, Windows

 

 

源码地址:

 

http://download.csdn.net/source/2393959

 


原文链接:http://blog.csdn.net/ostrichmyself/article/details/5623804

(###)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多