JAVA进程和属性了解

来源:互联网 发布:linux压缩成tar.gz 编辑:程序博客网 时间:2024/06/03 22:40

Java 进程间通信机制汇总

1.Socket

使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。

例如:

客户端:


[java] view plaincopyprint?
  1. import java.io.DataInputStream;  
  2. import java.io.DataOutputStream;  
  3. import java.io.IOException;  
  4. import java.net.Socket;  
  5.   
  6.   
  7. public class Socket_Client {  
  8.   
  9.     public static void main(String[] args) throws InterruptedException {  
  10.         // TODO Auto-generated method stub  
  11.         Socket socket = null;  
  12.         String str = null;  
  13.         DataOutputStream out = null;  
  14.         DataInputStream in = null;  
  15.           
  16.         try{  
  17.             socket = new Socket("127.0.0.1"4331);  
  18.             in = new DataInputStream(socket.getInputStream());  
  19.             out = new DataOutputStream(socket.getOutputStream());  
  20.             for(int i = 0; i < 10; i=i+1)  
  21.             {  
  22.                 out.writeUTF("" + i);  
  23.                 str = in.readUTF();  
  24.                 System.out.println("收到:" + str);  
  25.                 Thread.sleep(500);  
  26.             }  
  27.               
  28.         }  
  29.         catch(IOException e){  
  30.             System.out.println(e.toString());  
  31.         }  
  32.           
  33.     }  
  34.   
  35. }  

服务器端:

[java] view plaincopyprint?
  1. import java.io.DataInputStream;  
  2. import java.io.DataOutputStream;  
  3. import java.net.ServerSocket;  
  4. import java.net.Socket;  
  5.   
  6.   
  7. public class Socket_Server {  
  8.   
  9.     public static void main(String[] args) {  
  10.         // TODO Auto-generated method stub  
  11.         ServerSocket server = null;  
  12.         Socket socket = null;  
  13.         String str = null;  
  14.         DataOutputStream out = null;  
  15.         DataInputStream in = null;  
  16.           
  17.         try{  
  18.             server = new ServerSocket(4331);  
  19.             System.out.println("等待用户呼叫");  
  20.             socket = server.accept();  
  21.             out = new DataOutputStream(socket.getOutputStream());  
  22.             in = new DataInputStream(socket.getInputStream());  
  23.               
  24.             while(true){  
  25.                 str = in.readUTF();  
  26.                 out.writeUTF("服务器呼叫");  
  27.                 System.out.println("收到:"+str);  
  28.                 Thread.sleep(500);  
  29.             }  
  30.         }  
  31.         catch(Exception e)  
  32.         {  
  33.             System.out.println(e.toString());  
  34.         }  
  35.   
  36.     }  
  37.   
  38. }  


2.RMI

RMI(Remote Method Invocation)远程方法调用是java的核心技术之一。是Enterprise JavaBeans的基础技术,是java建立分布式应用程序的强大支柱。RMI允许一个应用程序访问另外一个服务器或虚拟机上的对象,方法和服务,它使远程方法调用就像在本地调用一样简单。它为用户屏蔽了底层的网络传输细节,使用的时候只需适当处理异常即可。所以RMI是非常容易使用的,但同时是非常强大的。


例如:

接口:

[java] view plaincopyprint?
  1. import java.rmi.Remote;  
  2. import java.rmi.RemoteException;  
  3.   
  4. public interface HelloImp extends Remote{  
  5.       
  6.     public String helloWorld() throws RemoteException;   
  7.   
  8.     public String sayHelloToSomeBody(String someBodyName) throws RemoteException;   
  9.   
  10. }  


 

类:

[java] view plaincopyprint?
  1. import java.rmi.RemoteException;  
  2. import java.rmi.server.UnicastRemoteObject;  
  3.   
  4. public class Hello extends UnicastRemoteObject implements HelloImp{  
  5.       
  6.   
  7.     public Hello() throws RemoteException {   
  8.     }   
  9.   
  10.        
  11.     public String helloWorld() throws RemoteException {   
  12.         return "Hello World!";   
  13.     }   
  14.   
  15.        
  16.     public String sayHelloToSomeBody(String someBodyName) throws RemoteException {   
  17.         return "你好," + someBodyName + "!";   
  18.     }   
  19. }  


 

客户端:

[java] view plaincopyprint?
  1. import java.net.MalformedURLException;  
  2. import java.rmi.Naming;  
  3. import java.rmi.NotBoundException;  
  4. import java.rmi.RemoteException;  
  5.   
  6. public class RMI_Client {  
  7.     public static void main(String[] args) {  
  8.         try {   
  9.             //在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法   
  10.             HelloImp rhello =(HelloImp) Naming.lookup("rmi://localhost:8888/RHello");   
  11.             System.out.println(rhello.helloWorld());   
  12.             System.out.println(rhello.sayHelloToSomeBody("Hello!SomeBody"));   
  13.         } catch (NotBoundException e) {   
  14.             e.printStackTrace();   
  15.         } catch (MalformedURLException e) {   
  16.             e.printStackTrace();   
  17.         } catch (RemoteException e) {   
  18.             e.printStackTrace();     
  19.         }   
  20.     }  
  21. }  


 

服务器端:


 

[java] view plaincopyprint?
  1. import java.net.MalformedURLException;  
  2. import java.rmi.AlreadyBoundException;  
  3. import java.rmi.Naming;  
  4. import java.rmi.RemoteException;  
  5. import java.rmi.registry.LocateRegistry;  
  6.   
  7. public class RMI_Server {  
  8.   
  9.     public static void main(String[] args) {  
  10.         // TODO Auto-generated method stub  
  11.         try {   
  12.             //创建一个远程对象   
  13.             HelloImp rhello = new Hello();   
  14.               
  15.             //本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少(Java默认端口是1099),必不可缺的一步,缺少注册表创建,则无法绑定对象到远程注册表上   
  16.             LocateRegistry.createRegistry(8888);   
  17.   
  18.             //把远程对象注册到RMI注册服务器上,并命名为RHello   
  19.             //绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的)   
  20.             Naming.bind("rmi://localhost:8888/RHello",rhello);   
  21.   
  22.             System.out.println(">>>>>INFO:远程Hello对象绑定成功!");   
  23.         } catch (RemoteException e) {   
  24.             System.out.println("创建远程对象发生异常!");   
  25.             e.printStackTrace();   
  26.         } catch (AlreadyBoundException e) {   
  27.             System.out.println("发生重复绑定对象异常!");   
  28.             e.printStackTrace();   
  29.         } catch (MalformedURLException e) {   
  30.             System.out.println("发生URL畸形异常!");   
  31.             e.printStackTrace();   
  32.         }   
  33.   
  34.     }  

总结:
从上面的过程来看,RMI对服务器的IP地址和端口依赖很紧密,但是在开发的时候不知道将来的服务器IP和端口如何,但是客户端程序依赖这个IP和端口。
这也是RMI的局限性之一。这个问题有两种解决途径:一是通过DNS来解决,二是通过封装将IP暴露到程序代码之外。
RMI的局限性之二是RMI是Java语言的远程调用,两端的程序语言必须是Java实现,对于不同语言间的通讯可以考虑用Web Service或者公用对象请求代理体系(CORBA)来实现。


3.内存映射

Java 利用NIO(New I/O)来实现共享内存,就是将不同进程的内存映射文件关联到同一个物理文件,NIO的内存映射文件(MappedByteBuffer)总是与某个物理文件相关,不管是从FileInputStream、FileOutputStream之类还是RandomAccessFile得来的FileChannel,都由map()得到的内存映射文件MappedByteBuffer。严格来说,其实就是两个Java进程通过中间文件来交换数据,用中间文件使得两个进程的两块内存区域的内容得到及时的同步。

代码 WriteShareMemory.java 往映射文件中依次写入 A、B、C ... Z,ReadShareMemory.java 逐个读出来,打印到屏幕上。代码对交换文件 swap.txt 的第一个字节作了读写标志,分别是 0-可读,1-正在写,2-可读。RandomAccessFile 得到的 Channel 能够灵活的进行读或写,并且不会破坏原有文件内容,而 FileInputStream 或 FileOutputStream 取得的 Channel 则很难达到这一功效,所以使用了 RandomAccessFile 来获得 FileChannel。

WriteShareMemory.java:

[java] view plaincopyprint?
  1. import java.io.RandomAccessFile;  
  2. import java.nio.MappedByteBuffer;  
  3. import java.nio.channels.FileChannel;  
  4. import java.nio.channels.FileChannel.MapMode;  
  5.    
  6. /** 
  7.  * 往 "共享内存" 写入数据 
  8.  * @author Unmi 
  9.  */  
  10. public class WriteShareMemory {  
  11.    
  12.     /** 
  13.      * @param args 
  14.      * @throws Exception 
  15.      */  
  16.     public static void main(String[] args) throws Exception {  
  17.         RandomAccessFile raf = new RandomAccessFile("f:/swap.txt""rw");  
  18.         FileChannel fc = raf.getChannel();  
  19.         MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 01024);  
  20.    
  21.         //清除文件内容  
  22.         for(int i=0;i<1024;i++){  
  23.             mbb.put(i,(byte)0);  
  24.         }  
  25.    
  26.         //从文件的第二个字节开始,依次写入 A-Z 字母,第一个字节指明了当前操作的位置  
  27.         for(int i=65;i<91;i++){  
  28.             int index = i-63;  
  29.             int flag = mbb.get(0); //可读标置第一个字节为 0  
  30.             if(flag != 0){ //不是可写标示 0,则重复循环,等待  
  31.                 i --;  
  32.                 continue;  
  33.             }  
  34.             mbb.put(0,(byte)1); //正在写数据,标志第一个字节为 1  
  35.             mbb.put(1,(byte)(index)); //写数据的位置  
  36.    
  37.             System.out.println("程序 WriteShareMemory:"+System.currentTimeMillis() +  
  38.                     ":位置:" + index +" 写入数据:" + (char)i);  
  39.    
  40.             mbb.put(index,(byte)i);//index 位置写入数据  
  41.             mbb.put(0,(byte)2); //置可读数据标志第一个字节为 2  
  42.             Thread.sleep(513);  
  43.         }  
  44.         raf.close();  
  45.     }  
  46. }  

ReadSharedMemory.java:

[java] view plaincopyprint?
  1. import java.io.RandomAccessFile;  
  2. import java.nio.MappedByteBuffer;  
  3. import java.nio.channels.FileChannel;  
  4. import java.nio.channels.FileChannel.MapMode;  
  5.    
  6. /** 
  7.  * 从 "共享内存" 读出数据 
  8.  * @author Unmi 
  9.  */  
  10. public class ReadShareMemory {  
  11.    
  12.     /** 
  13.      * @param args 
  14.      * @throws Exception 
  15.      */  
  16.     public static void main(String[] args) throws Exception {  
  17.         RandomAccessFile raf = new RandomAccessFile("f:/swap.txt""rw");  
  18.         FileChannel fc = raf.getChannel();  
  19.         MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 01024);  
  20.         int lastIndex = 0;  
  21.    
  22.         for(int i=1;i<27;i++){  
  23.             int flag = mbb.get(0); //取读写数据的标志  
  24.             int index = mbb.get(1); //读取数据的位置,2 为可读  
  25.    
  26.             if(flag != 2 || index == lastIndex){ //假如不可读,或未写入新数据时重复循环  
  27.                 i--;  
  28.                 continue;  
  29.             }  
  30.    
  31.             lastIndex = index;  
  32.             System.out.println("程序 ReadShareMemory:" + System.currentTimeMillis() +  
  33.                     ":位置:" + index +" 读出数据:" + (char)mbb.get(index));  
  34.    
  35.             mbb.put(0,(byte)0); //置第一个字节为可读标志为 0  
  36.    
  37.             if(index == 27){ //读完数据后退出  
  38.                 break;  
  39.             }  
  40.         }  
  41.         raf.close();  
  42.     }  
  43. }  

 

Java的多进程运行模式分析

一般我们在java中运行其它类中的方法时,无论是静态调用,还是动态调用,都是在当前的进程中执行的,也就是说,只有一个java虚拟机实例在运行。而有的时候,我们需要通过java代码启动多个java子进程。这样做虽然占用了一些系统资源,但会使程序更加稳定,因为新启动的程序是在不同的虚拟机进程中运行的,如果有一个进程发生异常,并不影响其它的子进程。

  在Java中我们可以使用两种方法来实现这种要求。最简单的方法就是通过Runtime中的exec方法执行java classname.如果执行成功,这个方法返回一个Process对象,如果执行失败,将抛出一个IOException错误。下面让我们来看一个简单的例子。

  // Test1.java文件

  import java.io.*;

  public class Test

  {

  public static void main(String[] args)

  {

  FileOutputStream fOut = new FileOutputStream("c://Test1.txt");

  fOut.close();

  System.out.println("被调用成功!");

  }

  }

  // Test_Exec.java

  public class Test_Exec

  {

  public static void main(String[] args)

  {

  Runtime run = Runtime.getRuntime();

  Process p = run.exec("java test1");

  }

  }

  通过java Test_Exec运行程序后,发现在C盘多了个Test1.txt文件,但在控制台中并未出现"被调用成功!"的输出信息。因此可以断定,Test已经被执行成功,但因为某种原因,Test的输出信息未在Test_Exec的控制台中输出。这个原因也很简单,因为使用exec建立的是Test_Exec的子进程,这个子进程并没有自己的控制台,因此,它并不会输出任何信息。

  如果要输出子进程的输出信息,可以通过Process中的getInputStream得到子进程的输出流(在子进程中输出,在父进程中就是输入),然后将子进程中的输出流从父进程的控制台输出。具体的实现代码如下如示:

  // Test_Exec_Out.java

  import java.io.*;

  public class Test_Exec_Out

  {

  public static void main(String[] args)

  {

  Runtime run = Runtime.getRuntime();

  Process p = run.exec("java test1");

  BufferedInputStream in = new BufferedInputStream(p.getInputStream());

  BufferedReader br = new BufferedReader(new InputStreamReader(in));

  String s;

  while ((s = br.readLine()) != null)

  System.out.println(s);

  }

  }

  从上面的代码可以看出,在Test_Exec_Out.java中通过按行读取子进程的输出信息,然后在Test_Exec_Out中按每行进行输出。 上面讨论的是如何得到子进程的输出信息。那么,除了输出信息,还有输入信息。既然子进程没有自己的控制台,那么输入信息也得由父进程提供。我们可以通过Process的getOutputStream方法来为子进程提供输入信息(即由父进程向子进程输入信息,而不是由控制台输入信息)。我们可以看看如下的代码:

  // Test2.java文件

  import java.io.*;

  public class Test

  {

  public static void main(String[] args)

  {

  BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

  System.out.println("由父进程输入的信息:" + br.readLine());

  }

  }

  // Test_Exec_In.java

  import java.io.*;

  public class Test_Exec_In

  {

  public static void main(String[] args)

  {

  Runtime run = Runtime.getRuntime();

  Process p = run.exec("java test2");

  BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));

  bw.write("向子进程输出信息");

  bw.flush();

  bw.close(); // 必须得关闭流,否则无法向子进程中输入信息

  // System.in.read();

  }

  }

  从以上代码可以看出,Test1得到由Test_Exec_In发过来的信息,并将其输出。当你不加bw.flash()和bw.close()时,信息将无法到达子进程,也就是说子进程进入阻塞状态,但由于父进程已经退出了,因此,子进程也跟着退出了。如果要证明这一点,可以在最后加上System.in.read(),然后通过任务管理器(在windows下)查看java进程,你会发现如果加上bw.flush()和bw.close(),只有一个java进程存在,如果去掉它们,就有两个java进程存在。这是因为,如果将信息传给Test2,在得到信息后,Test2就退出了。在这里有一点需要说明一下,exec的执行是异步的,并不会因为执行的某个程序阻塞而停止执行下面的代码。因此,可以在运行test2后,仍可以执行下面的代码。

  exec方法经过了多次的重载。上面使用的只是它的一种重载。它还可以将命令和参数分开,如exec("java.test2")可以写成exec("java", "test2")。exec还可以通过指定的环境变量运行不同配置的java虚拟机。

  除了使用Runtime的exec方法建立子进程外,还可以通过ProcessBuilder建立子进程。ProcessBuilder的使用方法如下:

  // Test_Exec_Out.java

  import java.io.*;

  public class Test_Exec_Out

  {

  public static void main(String[] args)

  {

  ProcessBuilder pb = new ProcessBuilder("java", "test1");

  Process p = pb.start();

  … …

  }

  }

  在建立子进程上,ProcessBuilder和Runtime类似,不同的ProcessBuilder使用start()方法启动子进程,而Runtime使用exec方法启动子进程。得到Process后,它们的操作就完全一样的。

  ProcessBuilder和Runtime一样,也可设置可执行文件的环境信息、工作目录等。下面的例子描述了如何使用ProcessBuilder设置这些信息。

  ProcessBuilder pb = new ProcessBuilder("Command", "arg2", "arg2", ''');

  // 设置环境变量

  Map<String, String> env = pb.environment();

  env.put("key1", "value1");

  env.remove("key2");

  env.put("key2", env.get("key1") + "_test");

  pb.directory("……/abcd"); // 设置工作目录

  Process p = pb.start(); // 建立子进程

Java中获取进程ID以及杀死进程的方法

taskkill介绍

taskkill是用来终止进程的。具体的命令规则如下:  TASKKILL [/S system [/U username [/P [password]]]]  { [/FI filter] [/PID processid | /IM imagename] } [/F] [/T]  描述:  这个命令行工具可用来结束至少一个进程。  可以根据进程 id 或图像名来结束进程。  参数列表:  

/S system 指定要连接到的远程系统。  

/U [domain\]user 指定应该在哪个用户上下文  执行这个命令。  

/P [password] 为提供的用户上下文指定密码。如果忽略,提示输入。  

/F 指定要强行终止的进程。  /FI filter 指定筛选进或筛选出查询的的任务。  

/PID process id 指定要终止的进程的PID。  

/IM image name 指定要终止的进程的图像名。通配符 '*'可用来指定所有图像名。  

/T Tree kill: 终止指定的进程和任何由此启动的子进程。  

/? 显示帮助/用法。  筛选器:  筛选器名 有效运算符 有效值

Java实现

private String getPID() {
  String pid = null;
  String cmd = "tasklist /nh /FI \"IMAGENAME eq eclipse.exe\"";
  try {
   Runtime runtime = Runtime.getRuntime();
   Process process = runtime.exec(cmd);
   BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
   String line = null;
   while((line=br.readLine()) != null){
    if(line.indexOf("eclipse.exe") != -1){
     String[] lineArray = line.split(" ");
     pid = lineArray[17].trim();
     if(pid.equals(RFT_ECLIPSE_PID)){
      continue;
     } else {
      return pid;
     }
    }
   }
  } catch (IOException e) {
   e.printStackTrace();
  }
  return pid;
 }

 

      try {
       RBD_ECLIPSE_PID = getPID();
       Runtime.getRuntime().exec("taskkill /F /PID "+RBD_ECLIPSE_PID);
      } catch (IOException e1) {
       e1.printStackTrace();
      }

 

获取当前运行Java的进程标识符(PID)

C:\Users>jps
7152 Jps
4440 后面是应用程序启动类的类名

C:\Users>

可以在新建一个批处理,在文件中输入JPS,然后在JAVA中用ProcessBuilder执行批处量命令后得到进程对象,通过输入对象可以读取到控制台中的内容.如下所示:

ProcessBuilder此类用于创建操作系统进程。

每个 ProcessBuilder 实例管理一个进程属性集。start() 方法利用这些属性创建一个新的Process 实例。start() 方法可以从同一实例重复调用,以利用相同的或相关的属性创建新的子进程。

 

修改进程构建器的属性将影响后续由该对象的 start() 方法启动的进程,但从不会影响以前启动的进程或 Java 自身的进程。

ProcessBuilder pb = new ProcessBuilder("批处理文件名带后缀");//该文件在工程目录下就行了.

pb.directory(System.getProperty("user.dir") );//得到当前工程所在的绝对路径.

Map<String, String> env = pb.environment();

env.put("VAR1", "myValue");

env.remove("OTHERVAR");

env.put("VAR2", env.get("VAR1") + "suffix");

Process p = pb.start();

  BufferedInputStream in = new BufferedInputStream(p.getInputStream());

  BufferedReader br = new BufferedReader(new InputStreamReader(in));

  String s;

  while ((s = br.readLine()) != null)

  System.out.println(s);

  }

  }

 

 

 

启动java并得到当前JVM进程的pid

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;

public class ShowOwnPID {
    public static void main(String[] args) throws Exception {
        int pid = getPid();
        System.out.println("pid: " + pid);
        System.in.read(); // block the program so that we can do some probing on it
    }
   
    private static int getPid() {
        RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
        String name = runtime.getName(); // format: "pid@hostname"
        try {
            return Integer.parseInt(name.substring(0, name.indexOf('@')));
        } catch (Exception e) {
            return -1;
        }
    }
}

 

Java的系统环境变量

java.version

Java 运行时环境版本

java.vendor

Java 运行时环境供应商

java.vendor.url

Java 供应商的 URL

java.home

Java 安装目录

java.vm.specification.version

Java 虚拟机规范版本

java.vm.specification.vendor

Java 虚拟机规范供应商

java.vm.specification.name

Java 虚拟机规范名称

java.vm.version

Java 虚拟机实现版本

java.vm.vendor

Java 虚拟机实现供应商

java.vm.name

Java 虚拟机实现名称

java.specification.version

Java 运行时环境规范版本

java.specification.vendor

Java 运行时环境规范供应商

java.specification.name

Java 运行时环境规范名称

java.class.version

Java 类格式版本号

java.class.path

Java 类路径

java.library.path

加载库时搜索的路径列表

java.io.tmpdir

默认的临时文件路径

java.compiler

要使用的 JIT 编译器的名称

java.ext.dirs

一个或多个扩展目录的路径

os.name

操作系统的名称

os.arch

操作系统的架构

os.version

操作系统的版本

file.separator

文件分隔符(在 UNIX 系统中是“/”)

path.separator

路径分隔符(在 UNIX 系统中是“:”)

line.separator

行分隔符(在 UNIX 系统中是“/n”)

user.name

用户的账户名称

user.home

用户的主目录

user.dir

用户的当前工作目录


可以通过System.getenv()获取系统环境变量;

System.getenv("JAVA_HOME"); // 获取JAVA_HOME环境变量

System.getenv("CLASSPATH"); // 获取CLASSPATH环境变量


-----------------------------------------------------------------------------------

Java启动时,如果设置了-classpath或-cp参数的话,直接取这个参数后面的值,设置到java.class.path系统变量中

否则查询CLASSPATH环境变量的值,设置到java.class.path系统变量中

如果没有定义CLASSPATH环境变量或CLASSPATH环境变量为.,则把.设置到java.class.path系统变量中

在Lanuer生成AppClassLoader类加载器的时候,处理如下

  String str = System.getProperty("java.class.path");  // 先拿到类加载路径
  File[] arrayOfFile = str == null ? new File[0] : Launcher.getClassPath(str); // 转换为File

  URL[] arrayOfURL = this.val$s == null ? new URL[0] : Launcher.pathToURLs(arrayOfFile); // 把File转换成URL


  private static URL[] pathToURLs(File[] paramArrayOfFile)
  {
    URL[] arrayOfURL = new URL[paramArrayOfFile.length];
    for (int i = 0; i < paramArrayOfFile.length; i++) {
      arrayOfURL[i] = getFileURL(paramArrayOfFile[i]); // 获取URL
    }


    return arrayOfURL;
  }


  static URL getFileURL(File paramFile)
  {
    try
    {
      paramFile = paramFile.getCanonicalFile();  // 如果File为.,则直接返回的URL为当前目录,如D:\work_taobao\workspace\Main

    } catch (IOException localIOException) {
    }
    try {
      return ParseUtil.fileToEncodedURL(paramFile);
    } catch (MalformedURLException localMalformedURLException) {
    }
    throw new InternalError();
  }

 

原创粉丝点击