本站首页 返回顶部 关于博主

让程序只能加载一次

PDF版
很多时候,我们只允许某个程序加载一次。也就是说,不允许这个程序的两个进程同时运行。 要实现这个功能中,该怎么做呢?本文将讨论Java中是如何实现的。 我找到了如下几个备选方案。
 
1.在临时文件中标记程序的状态。
此方法提出者的观点是这样的:当程序运行时,在某个文件临时文件中写一个标记1;在程序退出时把这个标记改成0。在程序加载时,检查这个文件中的标记值 是什么,如果是0,则意味这系统中没有程序的运行实例。此时可以继运行。如果是1,那就意味着程序已经加载了一个实例,这时就可以直接退出了。 理论上似乎是可以的,但现实往往没那么完美。假设,当程序运行时,临时文件中的标记已经改为1了,这个时候程序异常退出,或者此时强行关掉Java虚拟机呢?程序显然会终止运行,但是临时文件中的标记却停留在了1,而不是期望中的0。当再次加载程序时,显然它会错误地认为已经有一个实例在运行了。 所以,这条路走不通。
 
2.从任务列表中查找进程名。
在DOS系统中,我们可以通过tasklist这个命令获取当前运行的所有进程。正好,Java给我们提供了一个运行可执行文件的方法,我们可以调用如下代码获取当前正在运行的所有进程。
Runtime.getRuntime().exec("tasklist");
于是,我们只要遍历所有的进程,只要发现进程列表中已经包含了程序名,那么就认为已经有一个实例在运行了。
但是,这样对吗?要知道,当我们运行java代码时,进程列表中显示的只是javaw.exe。也或者,把两个不同的可执行文件改成相同的文件名,在执行时,它们显示的也是相同的进程名。因此,这种方法也是靠不住的。
 
3.通过JNI调用系统本地代码。
如果通过C或C++代码实现这样的功能,那是轻而易举的事情。如果我们先用C或C++代码写好判断当前程序是否已经有一个实例,然后通过JNI来调用C或C++实现的代码即可。
这条路行得通。
 
4.占用端口。
如果不采用调用系统本地代码的方法,那么我们还有这种方法:占用端口。原理是当程序启动时,判断某个端口(往往设置成一个不常用的端口,如60000)是否被占用。如果占用,就证明此程序已经有一个实例在运行了。
当程序正常关闭或异常退出时,占用的端口会自动释放。
这条路也行得通,但必须注意端口号的选取。如果选取的是一个常用的端口或者正好其他的某个程序也使用了这个端口,那么这个方法就不工作了。
 
5.文件锁。
最后,还有一个种方法,文件锁。
它的原理和占用端口相同,唯一的区别就是把占用端口变成了给文件加锁。在占用端口这种方法中,重要的是选取一个不常用的端口,在文件锁方法中,重要的是选择一个尽量不会重复的文件。当程序运行时,判断此文件是否加锁,如果已经加锁,说明此程序已经有一个实例在运行了,否则就给它加锁。
当程序正常关闭或异常退出时,会自己解锁。
 
综上,如果对C或C++比较熟悉,或者不介意使用JNI,可以通过JNI调用本地代码的方法。如果只想使用Java实现,那么最好使用文件锁,当然,占用端口也可以。



请你留言