用过金山词霸的人可能都会发现,当你把金山词霸打开,如果再去双击金山词霸 的快捷方式来启动它的时候,系统没有为我们再启动一个新的进程而是激活了前面我们已经打开的金山词霸程序,也就是说我们在一台计算机上同一时刻只能运行一 个金山词霸的实例。这样的软件现在也很多,典型的还有winamp,暴风影音等…..这样的好处可以避免用户因为误点而启动了原本不想启动的程序,同时也 使得我们的软件不会出现因为访问同一个资源而引起内部冲突问题。那么我们能不能在Java中也实现同样的功能呢?
[xhse9_w
有的人可能会说了,我用单一模式去实现我的Java类不就好了吗?其实单一模式只是保证了一个类在其他的类中只能构造一个实例,并不能实现只有一个系统进 程在运行的功能。要实现这个功能的话,其实只要考虑一下用C++应该怎么样实现就好了,因为有了JNI,只要能用C++实现,稍微变一下的话就可以在 Java中实现了。下面我就以一个例子来说明我自己的实现方法。如果用户试图运行这个程序的第二个实例,那么就会弹出一个“只能有一个程序的实例被运 行!”的警告然后终止进程。具体的效果图如下:
c! YR,)
第一次运行程序
>&B'y4'a~
打开第二个进程实例(第一个没有关闭)
ZrkMZkV Pf
在Java程序中
C<2C}@[Nx
先要定义一个将要在C++中具体实现的本地方法
; "citGK
[5ccn5}P"
// 得到当前本程序正在运行的实例的数目
Y${kf-N/
private native int getInstanceSum();
6rYYb?-
x&+ne
然后再在Java程序的构造函数中去调用这个方法,如果函数返回值不为0,说明已经有实例在运行了,这个时候就要做相应的处理,否则正常进行构造函数的执行。具体的代码如下:
}mlf DE
Copy code
package com.caokai.jni;
*28h
m -]xcv
import javax.swing.JFrame;
G3+3pQ
import javax.swing.JOptionPane;
v"vT eWS
p0jBC
public class SingletonInstance extends JFrame {
(f"5L
;D VaS&
static
= o g;Mrqt
{
3}=c;([B
try
#'+nR iu;V
{
(/u}B
System.loadLibrary("SingletonInstance");
@6NyzQM
}
Dq(F|0Qe
catch(Exception ex)
k}}fI)y
{
qz?Us &k
ex.printStackTrace();
smmk6uZg5
}
[FVoM
}
YR WrIZrm
zMx UuK
private native int getInstanceSum();
~=r^]}:~
vI+BR7s;=
/**
B-S5^ /<
* Method main
tR*;fy(,0
*
q7 zpuY?
*
}|k"M|
* @param args
@{M!j%/D
*
1$C}] U>
*/
X&|L9uM.g
public static void main(String[] args)
[D5k%>
{
gfYt@6r
eK|B>/OV
SingletonInstance instance = new SingletonInstance();
.P,[:dRn
instance.pack();
In"v(KI8v
instance.setVisible(true);
T;vnawG;
}
KI ,*]Yr
%=bz +x(07
/**
=YrV`~$n&
* Method SingletonInstance
5HE]:%#)
*
f [5n
* Defult constructor.
$F7L]fh
*/
=+`jxFMRl
public SingletonInstance()
e !K]u}
{
ch Q( >F
1_b3ht&M
super("SingletonInstance");
_9wJPNj
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
5vK0FIf
checkRunning();
@oprP%n
}
e{1842a
8 5EJL T
/**
BLH916/
* Method checkRunning
qBreaR$)oY
*
]O$}` Mh
* Check whether one instance of this program is running.
(Mjp;d<2g
*/
7#phcm
public void checkRunning()
{q](t n
{
Sl*o:_r i
if(getInstanceSum() == 1)
F<lTHL9
{
R {%1 W
JOptionPane.showMessageDialog(null, "只能有一个程序的实例被运行!");
R=e~qBV)
System.exit(0);
@p[e}_t(
}
|x=NR=Ph*
}
[h&w 0z,Z
}
(Nn-
fR`lj6
v<-:uZi}T
在C++程序中
9;"]M:Pv
Z Y%RjF
具 体的去实现前面声明的getInstanceSum() 函数。我们知道每一个windows程序在运行的时候,系统就会为其启动的进程分配4G的虚拟空间,然后再这个空间里面去启动进程里面的线程去完成程序具 体的功能。当然首先启动的就是main 函数的线程,就是主线程,然后再由主线程去启动其他的子现程。只要我们为主线程创建一个命名的互斥对象,这样当有第二个实例在运行的时候,当其创建同一个 名字的互斥对象时就会返回一个ERROR_ALREADY_EXISTS错误,如果我们调用GetLastError() 方法返回的错误和 ERROR_ALREADY_EXISTS相同,就说明已经有一个实例在运行了,这样就可以判断是否只有一个实例在运行了。具体的代码如下:
llVA,v2(9
@ 79ZF N%S #include <windowsh>
N",`zL0>
#include "SingletonInstance.h"
O;AE{eM~_#
Vwi;#%
HANDLE hMutex;
-=7 00
hB0 n
JNIEXPORT jint JNICALL Java_com_caokai_jni_SingletonInstance_getInstanceSum
0G>l>a4
(JNIEnv *env, jobject obj)
T!-_b"2?
{
EqZX?Rgs,
hMutex = CreateMutex(NULL, FALSE, "SingletonInstance");
vz6o&Be[
if(hMutex)
JF8ILB mO'
{
^nsvQ,F
if(GetLastError() == ERROR_ALREADY_EXISTS)
E^hB]EC
{
;KCFA44 t
return JNI_TRUE;
-L)M,{[@9
}
I/B67v{]74
}
M teFMzm
c ,i=}e+
return JNI_FALSE;
R|Nh7`=
}
8 W Xw'