前言
在项目中,为了实现“并发编程”(同时执行多个任务),就引入了“多进程编程”,把一个很大的任务,拆分成若干个很小的任务,创建多个进程,每个进程分别负责其中的一部分任务。
这也带来了一些问题:创建/销毁进程,比较低效,所以就引入了线程,每个线程都是一个独立的执行流,一个进程包含了一个或多个线程,创建线程/销毁线程 比 创建进程/销毁进程 更高效。
因此,在 Java 圈子中,大部分的并发编程都是通过多线程的方式实现的,但是进程也有线程比不上的优势 ----> 进程的独立性
操作系统中,同一时刻运行着很多个进程,如果某一个进程挂了,不会影响其他的进程(每个进程有各自的地址空间),相比之下,由于多个线程共用一个进程的地址空间,某个线程挂了,就有可能会把整个进程都祸害掉
所以对于使用进程还是线程实现并发编程,我们要根据项目的特性来考虑
多进程介绍
站在操作系统的角度(如 Linux )提供了很多和多进程编程相关的接口,比如:进程创建,进程终止,进程等待,进程程序替换,进程间通信 ......
而 Java 中对系统提供的这些操作进行了限制,最终给用户提供了两个操作:1.进程创建 2.进程等待
进程创建&等待
创建出一个新的进程,让这个新的进程来执行一系列的任务,被创建出来的进程称为子进程,创建子进程的进程称为父进程
代码演示
/*** 测试进程的创建* */
public class TestExec {public static void main(String[] args) throws IOException, InterruptedException {// Runtime 采用了单例模式,只能有一个实例Runtime runtime=Runtime.getRuntime();//创建并运行了一个进程,相当于在控制台输入了 javac 运行Process process=runtime.exec("javac");// javac 是一个控制台程序,它的输出是输出到“标准输出”和“标准错误”这两个特殊文件中的//要想看到这个程序的运行结果,就要去获取“标准输出”和“标准错误”这两个特殊文件中的内容//为什么在 idea 中看不到上述进程运行的结果?//一个进程在启动的时候会自动的打开 3 个文件:// 1.标准输入(对应键盘)// 2.标准输出(对应显示器)// 3.标准错误(对应显示器)//虽然子进程启动以后也打开了这 3 个文件,但是由于子进程没有和 idea 的终端关联,因此在 idea 中看不到子进程的输出,需要手动获取// 获取标准输出InputStream stdOutFrom=process.getInputStream();FileOutputStream stdOutTo=new FileOutputStream("stdOut.txt");while (true){int ch=stdOutFrom.read();if(ch==-1){break;}stdOutTo.write(ch);}stdOutFrom.close();stdOutTo.close();//获取标准错误InputStream stdErrFrom=process.getErrorStream();FileOutputStream stdErrTo=new FileOutputStream("stdError.txt");while (true){int ch=stdErrFrom.read();if(ch==-1){break;}stdErrTo.write(ch);}stdErrFrom.close();stdErrTo.close();//执行 Process 类的 waitFor 方法来实现进程的等待//父进程执行到 waitFor 方法时就会阻塞,阻塞到子进程执行完毕为止(和 Thread.join 是很像的)//返回的 exitCode 是退出码,退出码用于表示进程是否正确执行完毕,如果正确执行完毕就返回 0 ,非 0 代表进程出现异常int exitCode=process.waitFor();System.out.println(exitCode);}
}
执行以上代码,可以得到空的 stdOut.txt 文件,与如下所示的 stdError.txt 文件,说明我们成功创建了一个进程,执行了 javac 命令
注意,以上的演示代码相当于创建了一个进程执行了 javac 操作,相当于在控制器中输入了 javac 并执行,要是没有配置 jdk 的环境变量就得不了正确的结果,关于环境变量的配置推荐看
配置 JDK 环境变量(最简单)