Java程序單實(shí)例運(yùn)行的簡(jiǎn)單實(shí)現(xiàn)
需求
最近做了個(gè)java項(xiàng)目,功能完成后打包安裝了,發(fā)現(xiàn)可以點(diǎn)開(kāi)多個(gè)實(shí)例,因?yàn)樽烂骘@示托盤(pán),所以點(diǎn)一次就會(huì)出現(xiàn)一個(gè)托盤(pán),并且系統(tǒng)也多了好幾個(gè)javaw進(jìn)程,這樣的話就不能保證程序的健壯性了,所以需要做一個(gè)判斷讓程序只運(yùn)行一個(gè)實(shí)例。
實(shí)現(xiàn)方式
Java沒(méi)有提供這樣的機(jī)制。從操作系統(tǒng)的觀點(diǎn)來(lái)看,一個(gè)啟動(dòng)的Java Application僅僅是一個(gè)JVM的運(yùn)行實(shí)例。運(yùn)行相同Application的兩個(gè)實(shí)例,僅僅是運(yùn)行兩個(gè)無(wú)關(guān)的JVM。
只有讓多個(gè)運(yùn)行實(shí)例之間有一個(gè)既定的通訊機(jī)制就可以保證只有一個(gè)實(shí)例運(yùn)行。
因?yàn)橐紤]平臺(tái)無(wú)關(guān),java程序的實(shí)例控制不應(yīng)該使用系統(tǒng)的內(nèi)核對(duì)象來(lái)完成,那么我們就必須找到其它的、可以獨(dú)享的資源。實(shí)際上,一臺(tái)機(jī)器無(wú)論是在什么操作系統(tǒng)上,網(wǎng)絡(luò)端口都是獨(dú)享的,也就是說(shuō)基于網(wǎng)絡(luò)端口這個(gè)獨(dú)享的原理,我們可以很方便地讓我們的Java程序?qū)崿F(xiàn)在內(nèi)存里面只有一個(gè)運(yùn)行實(shí)例這個(gè)功能,而且這個(gè)功能的實(shí)現(xiàn)是與平臺(tái)無(wú)關(guān)的。
使用端口號(hào)控制的方式,先創(chuàng)建端口,運(yùn)行的時(shí)候再判斷端口是否被占用來(lái)判斷是否啟動(dòng)新實(shí)例。
文件鎖的方式,這種方式的用法在于運(yùn)行程序的時(shí)候?qū)⑽募湘i,然后判斷這個(gè)文件是否被鎖進(jìn)而來(lái)判斷是否要運(yùn)行一個(gè)新實(shí)例。
使用端口號(hào)+文件的方式,這種方式的用法在于啟動(dòng)的時(shí)候創(chuàng)建一個(gè)文件,關(guān)閉的時(shí)候刪掉這個(gè)文件,當(dāng)然僅僅這么一個(gè)操作不能起到上述要求的,如果非法關(guān)閉的話,文件還存在就不能滿足要求,只能是再加上一個(gè)端口的控制,即當(dāng)端口被占用并且文件存在的情況下就停止運(yùn)行新實(shí)例,否則啟動(dòng)一個(gè)實(shí)例,經(jīng)試驗(yàn)這種方式可以得到滿足。
代碼實(shí)現(xiàn)
第一種實(shí)現(xiàn)(端口控制)
//方案:使用java.net.ServerSocket //問(wèn)題:打開(kāi)服務(wù)端口可能會(huì)受到防火墻的影響;可能和別的端口沖突。 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..... } }
第二種實(shí)現(xiàn)(文件鎖)
/*方案:使用Java的加鎖文件機(jī)制,idea相當(dāng)簡(jiǎn)單,讓運(yùn)行實(shí)例通過(guò)java.nio.channels.FileLock獲得一個(gè)"well-known"文件的互斥鎖。*/ //存在的問(wèn)題:平臺(tái)相關(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() //問(wèn)題:文件可能因?yàn)槟承┰虿荒鼙粍h除,即使利用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; }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
mybatis TypeHandler注入spring的依賴(lài)方式
這篇文章主要介紹了mybatis TypeHandler注入spring的依賴(lài)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Kafka單節(jié)點(diǎn)偽分布式集群搭建實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了Kafka單節(jié)點(diǎn)偽分布式集群搭建實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11JAVA+Hibernate 無(wú)限級(jí)分類(lèi)
主要看menu_id和parent_id這兩個(gè)字段。 Eclipse生成的表持久映射:(說(shuō)明:自己加level屬性,作用:為了記錄種類(lèi)所在深度)2008-07-07springboot利用@Aspect實(shí)現(xiàn)日志工具類(lèi)的詳細(xì)代碼
這篇文章主要介紹了springboot利用@Aspect實(shí)現(xiàn)日志工具類(lèi),通過(guò)實(shí)例代碼介紹了導(dǎo)包及在啟動(dòng)類(lèi)上進(jìn)行注解自動(dòng)掃描的方法,需要的朋友可以參考下2022-03-03實(shí)例講解Java編程中數(shù)組反射的使用方法
這篇文章主要介紹了Java編程中數(shù)組反射的使用方法,通過(guò)編寫(xiě)數(shù)組反射工具類(lèi)可以重用許多基礎(chǔ)代碼,減少對(duì)類(lèi)型的判斷過(guò)程,需要的朋友可以參考下2016-04-04SpringBoot實(shí)現(xiàn)Thymeleaf驗(yàn)證碼生成
本文使用SpringBoot實(shí)現(xiàn)Thymeleaf驗(yàn)證碼生成,使用后臺(tái)返回驗(yàn)證碼圖片,驗(yàn)證碼存到session中后端實(shí)現(xiàn)校驗(yàn),前端只展示驗(yàn)證碼圖片。感興趣的可以了解下2021-05-05詳解Java如何應(yīng)對(duì)常見(jiàn)的安全威脅和攻擊類(lèi)型
隨著信息技術(shù)的快速發(fā)展,網(wǎng)絡(luò)安全問(wèn)題日益突出,本文將以Java開(kāi)發(fā)語(yǔ)言為例,深入探討網(wǎng)絡(luò)協(xié)議的安全性問(wèn)題,通過(guò)分析常見(jiàn)的安全威脅和攻擊類(lèi)型,設(shè)計(jì)和實(shí)施安全協(xié)議等主題,為讀者提供一些有益的思路和方法,需要的朋友可以參考下2023-11-11