java多进程——Java Multiple process (PID)

本文介绍如何使用Java启动多个子进程并实现进程间通信,包括输出信息的获取与输入信息的提供,同时对比了Runtime.exec与ProcessBuilder的不同用法。


Java的多进程运行模式分析

一般我们在java中运行其它类中的方法时,无论是静态调用,还是动态调用,都是在当前的进程中执行的,也就是说,只有一个java虚拟机实例在运行。而有的时候,我们需要通过java代码启动多个java子进程。这样做虽然占用了一些系统资源,但会使程序更加稳定,因为新启动的程序是在不同的虚拟机进程中运行的,如果有一个进程发生异常,并不影响其它的子进程。 
在Java中我们可以使用两种方法来实现这种要求。最简单的方法就是通过Runtime中的exec方法执行java classname。如果执行成功,这个方法返回一个Process对象,如果执行失败,将抛出一个IOException错误。下面让我们来看一个简单的例子。 
// Test1.java文件 

  import java.io.*; 

  public class Test1 { 
  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 Test2{ 
  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就退出了,而如果不用flush(),只有当缓冲区满的时候再回发送数据。在这里有一点需要说明一下,exec的执行是异步的,并不会因为执行的某个程序阻塞而停止执行下面的代码。因此,可以在运行test2后,仍可以执行下面的代码。 
  exec方法经过了多次的重载。上面使用的只是它的一种重载。它还可以将命令和参数分开,如exec("java.test2")可以写成exec("java", "test2")。exec还可以通过指定的环境变量运行不同配置的java虚拟机。 
  除了使用Runtime的exec方法建立子进程外,还可以通过ProcessBuilder建立子进程。访问平台专有信息并不是一件容易的事。虽然可以使用 Runtime.exec() 创建进程,但由于平台之间的差异,构造参数集常常令人头痛不已。Tiger提供了新的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(); // 建立子进程

关于工作路径:

假定在/bin下面有个可执行文件xxx,
我在shell里cd /home/abc,然后/bin/xxx来调用他,那么当前工作目录就是/home/abc
这时候fopen("a.txt", "r"),就是在/home/abc来找这个a.txt文件。
如果在执行过程中调用chdir("/home/abc/def")成功切换了目录,那么当前工作目录就变成了/home/abc/def
这时候fopen("a.txt", "r"),就是在/home/abc/def来找这个a.txt文件。

可以用:

System.getProperty( "java.class.path "); 
或者 
System.getProperty( "user.dir "); 

### Python 多进程处理教程 Python 的 `multiprocessing` 模块提供了创建和管理多个进程的功能,使得程序可以并行运行任务。以下是关于如何使用该模块的一些核心概念及其示例。 #### 创建子进程 通过 `multiprocessing.Process` 类可以直接启动一个新的进程来执行指定的任务函数。下面是一个简单的例子: ```python import multiprocessing def task_function(name): print(f"Task {name}: Running on PID {multiprocessing.current_process().pid}") if __name__ == "__main__": processes = [] for i in range(5): p = multiprocessing.Process(target=task_function, args=(f"Process-{i}",)) processes.append(p) p.start() for p in processes: p.join() ``` 此代码展示了如何创建五个独立的子进程,并让它们分别打印自己的名称和进程 ID[^1]。 --- #### 进程间通信 (共享数据) 当需要在不同进程中共享某些变量时,可以通过 `Value` 和 `Array` 来实现低级的数据共享;或者利用更高级别的工具如 `Manager()` 提供的对象来进行复杂结构的同步操作。 ##### 使用 Value 和 Array 共享基本类型数据 以下是如何在一个单独定义的目标函数里修改全局范围内的数值型或数组类型的实例: ```python from multiprocessing import Process, Value, Array def update_data(number, array): number.value += 10 for idx in range(len(array)): array[idx] *= -1 if __name__ == '__main__': shared_num = Value('i', 7) # 初始化整数为7 shared_arr = Array('d', [1.8, 2.9, 3.4]) proc = Process(target=update_data, args=(shared_num, shared_arr,)) proc.start(); proc.join(); print(shared_num.value) # 输出应增加到17 print([item for item in shared_arr]) ``` 这里演示了两个进程之间传递参数以及更新这些值的方法. --- ##### 利用 Manager 实现复杂的对象共享 对于列表、字典等较复杂的数据结构,则推荐采用 `Manager` 对象的方式进行跨进程间的协作. ```python from multiprocessing import Process, Manager def add_items(manager_list, index): manager_list[index] = str(index)*index if __name__ == '__main__': mgr = Manager() managed_lst = mgr.list(['A']*6) jobs = [] length = len(managed_lst) for pos in range(length): job = Process(target=add_items, args=(managed_lst,pos,)) jobs.append(job); job.start(); for j in jobs: j.join() print(list(managed_lst)) # 结果会显示被替换后的字符串形式索引号重复次数. ``` 这段脚本说明了怎样借助 Managers 让不同的 Processes 同步访问同一份 List 数据源[^3]. --- #### 并发控制:锁与信号量 为了防止竞争条件的发生,在多线程或多进程中通常需要用到锁定机制(Lock/Semaphore),以确保每次只有一个线程/进程能够进入临界区。 ##### Lock 锁定资源 如果希望保护特定部分不允许多个并发请求同时更改状态的话,那么就可以考虑引入 locks : ```python from multiprocessing import Process, Lock lock_instance = Lock() def safe_print(locker,msg): locker.acquire() # 请求获取独占权之前先尝试获得锁。 try: print(msg) finally: locker.release() # 不管发生什么都记得释放它! procs = [] for msg_text in ["First","Second"]: procs.append(Process(target=safe_print,args=(lock_instance,f"[{msg_text}]"))) [p.start() for p in procs]; [p.join() for p in procs] ``` 上面这个片段保证即使存在异步调用也不会造成消息交错输出的情况出现[^2]. --- #### Pool 批量提交任务 除了手动管理和分配各个 worker threads 或者 processes ,还可以依靠 pool 自动化完成大量相似工作的调度安排工作流程简化很多。 ```python from multiprocessing import Pool def square(x): return x*x with Pool(processes=4) as pool_obj: # 开启四个 workers results = pool_obj.map(square,[n for n in range(10)]) # map 方法自动分派输入序列给各节点计算平方根返回集合结果集 print(results) # 展示最终得到的一系列答案 ``` 上述案例表明了运用 pools 可极大程度减少编写繁琐循环逻辑的工作负担[^2]. --- ### 总结 以上介绍了几种常见的 python 中基于 multi-processing 库开发的技术手段,包括但不限于基础的 process creation , inter-process communication via values & arrays / managers plus concurrency control by means of locking mechanisms etc.. Each technique has its own advantages depending upon use case requirements thus choosing wisely among them will lead towards optimal performance gains while handling large scale computations involving multiple cores efficiently within your applications built using this powerful feature set provided out-of-the-box inside standard library itself !
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值