有一阵很羡慕Win下的某些本地程序:因为他们可以实现单进程运行——不是设计模式里面说的单例,那个Java也可以,而由于Java程序每次启动都会启动新的虚拟机……所以单例和单进程是不同的概念。
后来接触到了嵌入式DB……发现它可以自然的实现单例(机制貌似是文件锁定)……不过还是有些不足:他只能阻止第二个进程运行。但是不能向第一个进程传递消息(Win本地程序可以在启动第二个程序时,让第一个程序做最大化之类的动作)……
网上查了一下,还有另一种方法:基于Socket的单进程实现。优点是可以传递消息给已经运行的进程。缺点是需要网络连接,对网络设置有依赖(比如防火墙设置、端口占用情况等)
原理大概是这样:先向某个端口发消息,如果返回说已经有程序运行了。就执行某段代码,然后退出;如果没有返回就启动某端口的监听,等待消息。然后进入程序正常启动阶段。一旦有消息传过来,并且确认是同一个程序的另一个实例传过来的。就通知那个实例,已经有实例在运行了。然后运行另一段程序。
具体的实现代码如下:
/*SingleCases.java——单进程接口*/
package turnip.gray.singlecases;
public interface SingleCases {
//运行程序
void run();
//指定程序标识:程序运行前首先会检查此标识。如果当前运行程序的标识,与某个已运行程序的标识相同。则激活已运行监听,并向已运行程序发送消息,使其激活其他程序启动监听
void setAppe(Class<?> appe);
//设置已有实例运行时的动作监听
void setWhenAppIsRunning(WhenAppIsRunning whenAppIsRunning);
//设置有其他实例尝试运行时的动作监听
void setWhenOtherAppIsRunning(WhenOtherAppIsRunning whenOtherAppIsRunning);
}
/*SocketSingleCases.java——基于端口的单进程的实现*/
package turnip.gray.singlecases;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class SocketSingleCases implements SingleCases {
private int port;
private WhenAppIsRunning whenAppIsRunning;
private WhenOtherAppIsRunning whenOtherAppIsRunning;
private Class<?> appe;
private ServerSocket ListenServerSocket;
private ServerSocket requestServerSocket;
public SocketSingleCases(){
port=2048;
}
public WhenAppIsRunning getWhenAppIsRunning() {
return whenAppIsRunning;
}
public void setWhenAppIsRunning(WhenAppIsRunning whenAppIsRunning) {
this.whenAppIsRunning = whenAppIsRunning;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Override
public void run() {
// TODO Auto-generated method stub
if (isRunning()) {
whenAppIsRunning.doThis();
} else {
try {
ListenServerSocket = new ServerSocket(port);
requestServerSocket = new ServerSocket(port + 1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
runningFlag();
}
}
private void runningFlag() {
new Thread() {
public void run() {
while (true) {
isOtherRunning(read(listen(requestServerSocket)));
}
}
}.start();
new Thread() {
public void run() {
while (true) {
write(listen(ListenServerSocket));
}
}
}.start();
}
private boolean isRunning() {
boolean running = false;
if (read(request(port))) {
running = true;
write(request(port + 1));
}
return running;
}
private Socket request(int port) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", port);
socket.setSoTimeout(200);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return socket;
}
private Socket listen(ServerSocket serverSocket) {
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return socket;
}
private void write(Socket socket) {
OutputStream out = null;
OutputStreamWriter outr = null;
BufferedWriter bw = null;
try {
out = socket.getOutputStream();
outr = new OutputStreamWriter(out);
bw = new BufferedWriter(outr);
bw.write(appe.getName());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
bw.close();
outr.close();
out.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private boolean read(Socket socket) {
boolean running = false;
InputStream in = null;
InputStreamReader inr = null;
BufferedReader br = null;
try {
in = socket.getInputStream();
inr = new InputStreamReader(in);
br = new BufferedReader(inr);
running = appe.getName().equals(br.readLine());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
br.close();
inr.close();
in.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return running;
}
private void isOtherRunning(boolean running) {
if (running) {
whenOtherAppIsRunning.doThis();
}
}
public WhenOtherAppIsRunning getWhenOtherAppIsRunning() {
return whenOtherAppIsRunning;
}
public void setWhenOtherAppIsRunning(
WhenOtherAppIsRunning whenOtherAppIsRunning) {
this.whenOtherAppIsRunning = whenOtherAppIsRunning;
}
public Class<?> getAppe() {
return appe;
}
public void setAppe(Class<?> appe) {
this.appe = appe;
}
/*WhenAppIsRunning.java——简易的监听,当已经有程序运行时,执行doThis()*/
package turnip.gray.singlecases;
public interface WhenAppIsRunning {
void doThis();
}
/*WhenOtherAppIsRunning .java——简易的监听,当其他程序尝试运行时,执行doThis*/
package turnip.gray.singlecases;
public interface WhenOtherAppIsRunning {
void doThis();
}
/*
* DemoFrame.java
*
* Created on __DATE__, __TIME__
*/
package turnip.gray.singlecases.demo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import turnip.gray.singlecases.SingleCases;
import turnip.gray.singlecases.WhenAppIsRunning;
import turnip.gray.singlecases.WhenOtherAppIsRunning;
/**
*
* @author __USER__
*/
public class DemoFrame extends javax.swing.JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
/** Creates new form DemoFrame */
public DemoFrame() {
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
//GEN-BEGIN:initComponents
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
setDefaultCloseOperation(3);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(
getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(layout.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 400,
Short.MAX_VALUE));
layout.setVerticalGroup(layout.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 300,
Short.MAX_VALUE));
pack();
}// </editor-fold>
//GEN-END:initComponents
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
SingleCases singlecases;
final DemoFrame demoFrame;
singlecases=new SocketSingleCases();
demoFrame=new DemoFrame();
singlecases.setAppe(demoFrame.getClass());
singlecases.setWhenAppIsRunning(demoFrame.getWhenAppIsRunning());
singlecases.setWhenOtherAppIsRunning(demoFrame.getOtherWhenAppIsRunning());
singlecases.run();
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
demoFrame.setVisible(true);
}
});
}
public WhenAppIsRunning getWhenAppIsRunning(){
WhenAppIsRunning whenAppIsRunning=new WhenAppIsRunning(){
@Override
public void doThis() {
// TODO Auto-generated method stub
System.exit(0);
}
};
return whenAppIsRunning;
}
public WhenOtherAppIsRunning getOtherWhenAppIsRunning(){
WhenOtherAppIsRunning whenOtherAppIsRunning=new WhenOtherAppIsRunning(){
@Override
public void doThis() {
// TODO Auto-generated method stub
int i=MAXIMIZED_BOTH;
int j=getExtendedState()^i;
setExtendedState(j);
}
};
return whenOtherAppIsRunning;
}
//GEN-BEGIN:variables
// Variables declaration - do not modify
// End of variables declaration//GEN-END:variables
}