需求:
最近做了個java項目,功能完成後打包安裝了,發現可以點開多個執行個體,因為案頭顯示托盤,所以點一次就會出現一個托盤,並且系統也多了好幾個javaw進程,這樣的話就不能保證程式的健壯性了,所以需要做一個判斷讓程式只運行一個執行個體。
實現方式: Java沒有提供這樣的機制。從作業系統的觀點來看,一個啟動的Java Application僅僅是一個JVM的運行執行個體。運行相同Application的兩個執行個體,僅僅是運行兩個無關的JVM。
只有讓多個運行執行個體之間有一個既定的通訊機制就可以保證只有一個執行個體運行。
因為要考慮平台無關,java程式的執行個體控制不應該使用系統的核心對象來完成,那麼我們就必須找到其它的、可以獨享的資源。實際上,一台機器無論是在什麼作業系統上,網路連接埠都是獨享的,也就是說基於網路連接埠這個獨享的原理,我們可以很方便地讓我們的Java程式實現在記憶體裡面只有一個運行執行個體這個功能,而且這個功能的實現是與平台無關的。
使用連接埠號碼控制的方式,先建立連接埠,啟動並執行時候再判斷連接埠是否被佔用來判斷是否啟動新執行個體。 檔案鎖的方式,這種方式的用法在於運行程式的時候將檔案上鎖,然後判斷這個檔案是否被鎖進而來判斷是否要運行一個新執行個體。 使用連接埠號碼+檔案的方式,這種方式的用法在於啟動的時候建立一個檔案,關閉的時候刪掉這個檔案,當然僅僅這麼一個操作不能起到上述要求的,如果非法關閉的話,檔案還存在就不能滿足要求,只能是再加上一個連接埠的控制,即當連接埠被佔用並且檔案存在的情況下就停止運行新執行個體,否則啟動一個執行個體,經實驗這種方式可以得到滿足。
代碼實現: 第一種實現:連接埠控制
//方案:使用java.net.ServerSocket//問題:開啟服務連接埠可能會受到防火牆的影響;可能和別的連接埠衝突。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..... }}第二種實現:檔案鎖
/*方案:使用Java的加鎖檔案機制,idea相當簡單,讓運行執行個體通過java.nio.channels.FileLock獲得一個"well-known"檔案的互斥鎖。*///存在的問題:平台相關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()也有可能產生這種情況。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{ //建立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);// 建立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;}
參考資料:
讓java程式只運行一個執行個體
如何讓Java程式只運行一個執行個體
********************************************************************************結束語********************************************************************************************
我在寫這篇部落格的時候也是一名初學者,有任何疑問或問題請留言,或發郵件也可以,郵箱為:577328725@qq.com,我會儘早的進行更正及更改。
在我寫過的部落格中有兩篇部落格是對資源的整理,可能對大家都有協助,大家有興趣的話可以看看。。
下載資料整理——目錄:http://blog.csdn.net/fanxiaobin577328725/article/details/51894331
這篇部落格裡面是我關於我見到的感覺不錯的好資源的整理,裡麵包含了書籍及原始碼以及個人搜尋的一些資源,如果有興趣的可以看看,我會一直對其進行更新和添加。
優秀的文章&優秀的學習網站之收集手冊:http://blog.csdn.net/fanxiaobin577328725/article/details/52753638
這篇部落格裡面是我對於我讀過的,並且感覺有意義的文章的收集整理,純粹的個人愛好,大家感覺有興趣的可以閱讀一下,我也會時常的對其進行更新。
********************************************************************************感謝********************************************************************************************