-
JNI即是Java Native Interface,主要用来Java和其他语言之间的交互,多半是和平台依赖的调用、其他高级语言的库甚至是低级语言交互。相信大家平时在JDK中看到很多native的方法,但是自己写JNI应用的会确很少。这次因为在进行开发中,遇到需要Java调用一个现成的成熟的功能完善的C语言应用库,因此 JNI便成了Java和C库之间的桥的功能。既然是初探,那么下面我们就从HelloWorld开始吧:虽然Java是一个跨平台的语言,但是C语言是一个和平台密切相关的语言;所以针对Unix、Linux、Windows等不同的操作系统,C语言的预编译,动态库的创建等都有一些差别,也需要我们分别进行处理之。下面仅基于Sun的Unix平台Sun Solaris开发为例。
1.首先声明第一个Java native程序:HelloWorld.java。
public class HelloWorld {
static {
init();
}
private native int hello();
public int sayHello() {
return hello();
}
/**
* @param args
*/
public static void main(String[] args) {
HelloWorld hw = new HelloWorld();
System.out.println("Say in Java:" + hw.sayHello());
}
private static void init() {
System.out.println("Starting to load HelloWorld lib.");
try {
System.loadLibrary("HelloWorld");
} catch (Throwable t) {
System.out.println("Load unsuccessfully.");
t.printStackTrace();
System.exit(-1);
}
System.out.println("Load successfully.");
}
}
2.编译该Java文件。
javac HelloWorld.java
3.生成相应的C语言的HelloWorld.h头文件,javah HelloWorld,头文件内容如下:(其中,static Java方法和非static Java方法经过javah -jni生成的C头文件是有区别的,差异在于参数,static Java方法生成的相应的C方法的第二个参数是jclass类型,非static Java方法生成的相应的C方法的第二个参数是jobject类型。)
more HelloWorld.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: hello
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_HelloWorld_hello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
4.开始编写第一个c语言的JNI的本地(native)方法:
vi HelloWorld.c
#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>
JNIEXPORT jint JNICALL Java_HelloWorld_hello
(JNIEnv* env, jobject target)
{
printf("Hello World in C program!!!\n");
return 2;
}
5.生成Unix下的动态库.so文件:
cc -G -I. -I$JAVA_HOME/include -I$JAVA_HOME/include/solaris HelloWorld.c -o libHelloWorld.so
在当前目录下看到新生成的libHelloWorld.so文件。也许你会注意到我们在Java程序中System.loadLibrary()加载的是库HelloWorld,那么这其实是一个命名的规范而已。以lib开头的.so文件即是Unix下的动态库,而JNI加载器通过HelloWorld这个名字实际上希望加载的动态库是libHelloWorld.so。
6.设置环境变量,查看LD_LIBRARY_PATH并设置:
vi .cshrc
setenv LD_LIBRARY_PATH '.:/usr/local/lib:$LD_LIBRARY_PATH'
source .cshrc
这个环境变量设置的是Java的动态库加载器需要加载的C动态库所在的路径。因此,我们生成的libHelloWorld.so一定要在这个环境变量中设置,在这个例子中我就把当前目录.加到环境变量LD_LIBRARY_PATH了:)
7.执行HelloWorld文件,java HelloWorld,在标准输出上看到:
Starting to load HelloWorld lib.
Load successfully.
Hello World in C program!!!
Say in Java:2
一切Okey,如此的easy,一阵狂喜!接下来就是将这个例子运用于JNI的开发之中了。
Resource:
《Java Native Interface: Programmer's Guide and Specification》
http://java.sun.com/docs/books/jni/html/start.html#27008
http://linuxmafia.com/faq/Admin/ld-lib-path.html
https://www6.software.ibm.com/developerworks/cn/education/java/j-jni/tutorial/j-jni-2-15.html -
Java程序开发中有大量的可复用资源,可复用的公用类;有很多中间件已经帮助我们解决了多线程的问题,所以很多开发人员是不需要深入的涉及到这个话题的,而要自己去开发一个服务端的应用的时候,多线程的编程、调试、发布、问题跟踪就变得无可避免了。
多线程编程需要涉及到两个方向,一个是使用Java语言进行程序设计,必须了解语言本身对于线程的支持和其编程模型;另外一个是运行时的运行状况,更多的是基于现有的硬件、先有的操作系统的配置问题。所有的应用都必须运行一定的硬件配置环境、一定的操作系统之上,所以线程的运行时状态无可避免的要在应用设计阶段进行考虑了。
1.对于共享可变数据的互斥(什么时候使用关键字synchronized):Java虚拟机通过对象锁来实现互斥,达到多个线程在同一个共享数据上独立而互不干扰地工作。
private int sequence = 0;
public synchronized int getSequence(){
return sequence++;
}
线程间通讯,保证共享对象对于多线程访问是从一种一致的状态跃迁到另外一种一致的状态。
public class Worker implements Runnable{
private boolean running = false;
public void run(){
while(isRunning()){
//
}
}
public synchronized boolean isRunning(){
return this.running;
}
public synchronized void stop(){
this.running = false;
}
public synchronized void startRequest(){
this.running = true;
}
}
2.协作(什么时候使用wait和notify):主要是用于多线程为了同一个目标而共同协作性的工作,应用需要设计多线程的协作工作机制。一般性地是,线程需要反复检查某一个数据结构,在等待某些条件的发生,又避免忙等(busy-wait)的时候。
使用wait()是需要仔细阅读以下javadoc了,wait(0)的情况很特殊:线程一直处于等待,直到被唤醒(notify()或者notifyAll())。
private Object monitor = new Object();
public void run(){
synchronized(monitor){
while(){
try{
obj.wait()
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Java使用的同步机制是监视器,为了更好的使用同步机制,就需要虚拟机是怎么使用监视器的机制的?
Java监视器主要分成了三个区域,入口区、监视区、等待区,所有进入到监视器开始处的线程首先都是进入到入口去,(可能阻塞)等待成为监视区的持有者;监视区域同时只能被一个线程持有并执行,只有当一个线程执行完了监视区域动作(该线程将释放监视区域并推出监视器)或者执行了等待命令(该线程将释放监视区域进入到等待区,直到执行了唤醒命令才能重新持有监视区域)才能释放监视区域;只有那些持有监视区域的线程执行了等待命令之后就会进入到等待区。所以基本可以了解了监视区域的各个功能、线程进出的时机动作。而且分析互斥和协作情况下线程和监视器的工作状态基本也就清晰了。
1.互斥情况下,多线程将会在等待区阻塞的等待持有监视区域,因为同时只会有一个线程执行监视区域(代码即指令),监视区域主要是共享数据或者共享资源。(例外就是虚拟机实现不是基于时间片的,那么监视器就会用来协调多线程的执行策略,将不仅仅是共享数据或者共享资源了。)
2.协作情况下,监视器主要协调多个线程之间共同工作,即是,一个已经持有监视区域的线程,通过执行等待命令,释放监视区域进入到等待区,那么该线程阻塞并一直持续暂停状态,只有监视区域持有线程执行了唤醒该线程命令并释放监视区域之后该线程才能重新持有监视区域,直到该线程再次释放监视区域。
运行阶段,Java线程的线程模型又是什么样的呢,究竟需要应用设计开发人员需要注意什么呢?
这主要涉及到Java的内存模型(Memory Model)、JVM实现等,其中我正在使用的虚拟机是Sun基于Solaris系统的,其线程模型参考《Threading》。Java的线程在Java2都是操作系统的线程,并没有了"green threads"的概念了,每一个线程的申请在向JVM申请资源的同时也是在向操作系统申请资源,对于各家JVM在不同的操作系统之上的thread stack size大小也有一些差别,其中Sun JVM可以通过-XX:ThreadStackSize(-Xss)参数进行设置(在默认情况下,"Thread Stack Size (in Kbytes). (0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]").
在我们设计并发要求比较高的服务端应用的时候,线程将会变得相当宝贵了,而且就针对操作系统而言,一个进程所能申请的线程的数量也是限制的,所以如何使用和调度多线程是一个重要的环节。在系统运行阶段,进行调优的时候不可避免的要面对多线程的资源相关的几个关注点:thread stack size、thread local heap、garbage collection affects、intimate shared memory。
进行多线程的设计编码,要极力追求简洁原则,尽力将多线程的调度和编码和业务相关的逻辑进行解耦,这样多线程处理模块被抽象出来,可复用度变高,对于分析、调试、问题跟踪多线程的设计编码就不再变得臃肿复杂;避免复杂、过多的条件判断等待、wait-notify,线程意外退出或者形成死锁。
在今天的编程生活中,我们也要面对和解决多线程编程的调试、单元测试、跟踪等方面的问题。解决这些问题也是我们的乐趣之一:)
参考:
《Effective Java 中文版》
《深入Java虚拟机》
http://java.sun.com/docs/hotspot/threads/threads.html
http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
http://www.theserverside.com/tt/knowledgecenter/knowledgecenter.tss?l=ConcurrencyTestingJavaApps -
2004-12-28
EMF Build Type
http://download.eclipse.org/tools/emf/scripts/downloads-build-types.php
1.Releases
Releases是由开发团队公开发布的主要build版本,比如:"R1.0"。
这种builds是稳定的(stable)、经过测试的版本(tested release),但是它不会包含最近最
新的features和improvements。
Release Builds的版本号总是以"R"开头;Non-release builds一般都是build的日期来命名。
2.Stable Builds
Stable builds是能够确定满足大部分用户的integration builds版本(注:Stable builds首先是一个
integration builds,并有很好的稳定性,满足用户的基本需求)。经过短期的使用和评估(或者评审)
由architecture team把stable build从integration build中提取出来(原文:They are promoted from
integration build to stable build by the architecture team after they have been used for a
few days and deemed reasonably stable. )。
这种builds会紧紧跟随最新的开发进程,包含最新最新的features和bug fixes,当然同时可能会由许多
bug和缺陷。
开发团队希望能够发布这种builds来获取用户及时有价值的反馈。
3.Integration Builds
这是一个周期性的工作,各个component teams保证释放的component都处在了稳定、具有兼容性的状态。
每一个compenent必须在配置文件中配置下一次integration build中本component的版本号。在新的稳定
component版本release到build中,必须要进行build integration builds.Integration builds经过测试
之后就会成为Stable Builds.
4.Nightly Builds
over night build任何被release到the HEAD stream of the CVS repository的代码。
完全没有经过测试,存在大量的问题;一般情况下都不会很好的运行。
这一步为改项目的开发者而是实现的。
Note: Nightly builds are produced only as requested, and not necessarily every night,
by developers to build what was in HEAD.
5.Maintenance Builds
周期性的build,为了保持维护未来版本的发布的执行。Maintenance Builds并不一定是一个stable builds.
maintenance builds最后确定和release的时候,就成为了一个Release build.Maintenance builds的名字以
"M"开头,在稳定性方面仍然没有经过考验。如果一个版本是release candidate的时候(“RC”),那么
就是一个stable maintenance build. -
2004-10-11
修改Eclipse Workspace路径
eclipse的worspace的路径放置在根目录:
\configuration\org.eclipse.ui.ide\recentWorkspaces.xml
eg:









