Java程序單實例運行的簡單實現(xiàn)
需求
最近做了個java項目,功能完成后打包安裝了,發(fā)現(xiàn)可以點開多個實例,因為桌面顯示托盤,所以點一次就會出現(xiàn)一個托盤,并且系統(tǒng)也多了好幾個javaw進(jìn)程,這樣的話就不能保證程序的健壯性了,所以需要做一個判斷讓程序只運行一個實例。
實現(xiàn)方式
Java沒有提供這樣的機制。從操作系統(tǒng)的觀點來看,一個啟動的Java Application僅僅是一個JVM的運行實例。運行相同Application的兩個實例,僅僅是運行兩個無關(guān)的JVM。
只有讓多個運行實例之間有一個既定的通訊機制就可以保證只有一個實例運行。
因為要考慮平臺無關(guān),java程序的實例控制不應(yīng)該使用系統(tǒng)的內(nèi)核對象來完成,那么我們就必須找到其它的、可以獨享的資源。實際上,一臺機器無論是在什么操作系統(tǒng)上,網(wǎng)絡(luò)端口都是獨享的,也就是說基于網(wǎng)絡(luò)端口這個獨享的原理,我們可以很方便地讓我們的Java程序?qū)崿F(xiàn)在內(nèi)存里面只有一個運行實例這個功能,而且這個功能的實現(xiàn)是與平臺無關(guān)的。
使用端口號控制的方式,先創(chuàng)建端口,運行的時候再判斷端口是否被占用來判斷是否啟動新實例。
文件鎖的方式,這種方式的用法在于運行程序的時候?qū)⑽募湘i,然后判斷這個文件是否被鎖進(jìn)而來判斷是否要運行一個新實例。
使用端口號+文件的方式,這種方式的用法在于啟動的時候創(chuàng)建一個文件,關(guān)閉的時候刪掉這個文件,當(dāng)然僅僅這么一個操作不能起到上述要求的,如果非法關(guān)閉的話,文件還存在就不能滿足要求,只能是再加上一個端口的控制,即當(dāng)端口被占用并且文件存在的情況下就停止運行新實例,否則啟動一個實例,經(jīng)試驗這種方式可以得到滿足。
代碼實現(xiàn)
第一種實現(xiàn)(端口控制)
//方案:使用java.net.ServerSocket
//問題:打開服務(wù)端口可能會受到防火墻的影響;可能和別的端口沖突。
import java.io.*;
import java.net.*;
public class OneInstance_2
{
private static ServerSocket listenerSocket;
public static void main(String[] args)
{
try
{
listenerSocket = new ServerSocket(2004);
//At this point, no other socket may listen on port 2004.
}
catch(java.net.BindException e)
{
System.err.println("A previous instance is already running....");
System.exit(1);
}
catch(final IOException e) // an unexpected exception occurred
{
System.exit(1);
}
// Do some work here.....
}
}
第二種實現(xiàn)(文件鎖)
/*方案:使用Java的加鎖文件機制,idea相當(dāng)簡單,讓運行實例通過java.nio.channels.FileLock獲得一個"well-known"文件的互斥鎖。*/
//存在的問題:平臺相關(guān)
import java.io.*;
import java.nio.channels.*;
public class OneInstance_1 {
public static void main(String[] args) throws Exception {
FileLock lck = new FileOutputStream("C:\\flagFile").getChannel().tryLock();
if(lck == null) {
System.out.println("A previous instance is already running....");
System.exit(1);
}
System.out.println("This is the first instance of this program...");
// Do some work here.....
}
}
//方案3:使用File.createNewFile() and File.deleteOnExit()
//問題:文件可能因為某些原因不能被刪除,即使利用Runtime.addShutdownHook()也有可能產(chǎn)生這種情況。
import java.io.*;
public class OneInstance_3
{
public static void main(String[] args) throws Exception
{
File flagFile = new File("C:\\flagFile");
if(false == flagFile.createNewFile())
{
System.out.println("A previous instance is already running....");
System.exit(1);
}
flagFile.deleteOnExit();
System.out.println("This is the first instance of this program..."); // Do some work here.....
}
}
第三種方式(端口+文件鎖)
public static void main(String[] args) throws IOException
{
//創(chuàng)建lock.java文件
String filePath = new File("IDRCallDll").getAbsolutePath().substring(0,
new File("IDRCallDll").getAbsolutePath().lastIndexOf("\\"));
File getFile = new File(filePath + "\\" + "lock.java");
System.out.println(getFile.getPath());
//判斷端口是否被占用
boolean flag = isLoclePortUsing(20520);
System.out.println(flag);
//如果文件存在并且端口被占用則退出
if (getFile.exists() && flag)
{
new MyTray().showDialog();
System.exit(0);
}
try
{
Socket sock = new Socket("127.0.0.1", 20520);// 創(chuàng)建socket,連接20520端口
}
catch (Exception e)
{
System.out.println("端口被占用!");
}
final Class<?> clazz = (Class<?>) JavaCall.class;
final boolean isWindows = System.getProperty("os.name").contains(
"Windows");
final List<String> args1 = new ArrayList<String>();
args1.add(isWindows ? "javaw" : "java");
args1.add("-Xmx" + 128 + "M");
args1.add("-cp");
args1.add(System.getProperty("java.class.path"));
args1.add("-Djava.library.path="
+ System.getProperty("java.library.path"));
args1.add(clazz.getCanonicalName());
// logger.info("start " + args1.toString());
final ProcessBuilder pb = new ProcessBuilder(args1);
pb.redirectErrorStream(true);
try
{
/**
* 讀身份證信息程序
*/
pb.start();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
RandomAccessFile r = new RandomAccessFile(
filePath + "\\" + "lock.java", "rws");
FileChannel temp = r.getChannel();
FileLock fl = temp.lock();
}
/**
* 判斷端口是否被占用
* @param port
* @return
*/
public static boolean isLoclePortUsing(int port)
{
boolean flag = true;
try
{
flag = isPortUsing("127.0.0.1", port);
}
catch (Exception e)
{
}
return flag;
}
public static boolean isPortUsing(String host, int port) throws UnknownHostException
{
boolean flag = false;
InetAddress theAddress = InetAddress.getByName(host);
System.out.println(theAddress);
try
{
ServerSocket socket = new ServerSocket(port);
flag = true;
}
catch (IOException e)
{
System.out.println("beizhanyong");
}
return flag;
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
mybatis TypeHandler注入spring的依賴方式
這篇文章主要介紹了mybatis TypeHandler注入spring的依賴方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
Kafka單節(jié)點偽分布式集群搭建實現(xiàn)過程詳解
這篇文章主要介紹了Kafka單節(jié)點偽分布式集群搭建實現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-11-11
springboot利用@Aspect實現(xiàn)日志工具類的詳細(xì)代碼
這篇文章主要介紹了springboot利用@Aspect實現(xiàn)日志工具類,通過實例代碼介紹了導(dǎo)包及在啟動類上進(jìn)行注解自動掃描的方法,需要的朋友可以參考下2022-03-03
SpringBoot實現(xiàn)Thymeleaf驗證碼生成
本文使用SpringBoot實現(xiàn)Thymeleaf驗證碼生成,使用后臺返回驗證碼圖片,驗證碼存到session中后端實現(xiàn)校驗,前端只展示驗證碼圖片。感興趣的可以了解下2021-05-05

