很巧合,今天恰好有朋友寫信與我討論這個問題,他是從網上看到關于這個問題的一篇文章,該文章提出使用socket來解決該問題。下面是我回信中的一些片斷:
關于讓Java程序只運行一個實例的問題,其實質是JVM之間通信的問題,因此自然會想到使用Socket來傳遞信號,但就像文章中提到的那樣,如果程序在開始運行時ServerSocket監聽的端口已經被其它程序占用,那么程序的運行就會受到影響。而且我個人認為僅僅為了判斷程序運行的實例而開一個端口來偵聽,有點殺雞用牛刀的感覺。
我這里有一個方案可供選擇,其原理是使用文件鎖來鎖定一個代表程序運行實例的文件,當程序啟動時,首先鎖定該文件,后繼啟動的實例一旦發現該文件已被鎖定則提示出錯信息。(為什么不直接使用文件而是使用文件鎖來判斷呢?即:在程序啟動的時候生成一個文件而在程序退出時刪除這個文件,只要判斷該文件的存在與否就可以判斷實例的運行情況。這是因為我們不能確保刪除文件的操作一定能被執行到,程序是可能被強制關閉或異常退出的,而文件鎖不同,它是作為系統資源分配給JVM的,一旦JVM當掉,其資源會一并被操作系統回收,因此對文件的鎖定也會被消除。)
下面是該方案的源碼。
import?java.nio.channels.*;
import?java.io.*;
import?javax.swing.JOptionPane;
//應用實例控制類
public?class?InstanceControl
{
??FileLock?lock=null;
??//判斷該應用是否已啟動
??public?boolean?isRunning()
??{
????try
????{
??????//獲得實例標志文件
??????File?flagFile=new?File("instance");
??????//如果不存在就新建一個
??????if(!flagFile.exists())flagFile.createNewFile();
??????//獲得文件鎖
??????lock=new?FileOutputStream("instance").getChannel().tryLock();
??????//返回空表示文件已被運行的實例鎖定
??????if(lock==null)return?false;
????}catch(Exception?ex){ex.printStackTrace();}
????return?true;
??}
??public?static?void?main(String[]?args)
??{
????InstanceControl?ic=new?InstanceControl();
????if(ic.isRunning())
??????JOptionPane.showMessageDialog(null,"已存在該程序的實例!","提示",JOptionPane.OK_OPTION);
????else
??????MainClass.main(args);????????????
??}
}
下面是這篇文章的原文
?? 讓Java程序只運行一個實例??????????????????????????????????????????????????????????????
????????????????????????????????????????????出自:http://developer.ccidnet.com 梁邦勇 2003年01月12日 18:04????????????????????????????????????????????
一個程序可以在內存里面存在多個運行實例,比如,你可以打開多個微軟的Word程序。但是,有些時候我們需要控制程序運行的實例只有一個,也就是說,該程序同一時
刻在內存里面運行的只有一個實例。這樣當這個程序在內存中已經存在一個運行實例而用戶又再次運行了該程序的時候,有兩種結果,第一種結果是結束目前的運行實??
例,打開新運行的實例;第二種就是讓新運行的實例退出,原有的運行實例繼續運行。????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???????????????????????????????????????????????????????????????????????? 原理????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
因為任何時候只有一個實例,所以在實現這種功能的時候必須借助只能被獨享的資源。如果我們的程序是基于某個平臺的,那么就可以借助操作系統的內核對象來完成,
比如Windows操作系統就提供了CreateMutex這個API來創建一個獨享的內核對象。但是因為要考慮平臺無關,Java程序的實例控制不應該使用系統的內核對象來完成,那??
么我們就必須找到其它的、可以獨享的資源。實際上,一臺機器無論是在什么操作系統上,網絡端口都是獨享的,也就是說基于網絡端口這個獨享的原理,我們可以很方
便地讓我們的Java程序實現在內存里面只有一個運行實例這個功能,而且這個功能的實現是與平臺無關的。??????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???????????????????????????????????????????????????????????????????????? 實現????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
我們先來看看第一種情況是如何實現的,也就是說如果系統中已經存在運行實例的話,那么結束原有的運行實例,讓新實例運行。這個實現實例控制的Java類也是一個線
程,具體的實現如下:????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??import java.net.*;??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??public class InstanceControl extends Thread {??????????????????????????????????????????????????????????????????????????????????????????????????????
?? public void run() {????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????try{??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? Socket sock = new Socket("127.0.0.1",22222);????????????????????????????????????????????????????????????????????????????????????????????????????
??//創建socket,連接22222端口??????????????????????????????????????????????????????????????????????????????????????????????????????
????}????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????catch (Exception e)??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????{}????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????try{??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? ServerSocket server = new ServerSocket(22222);//創建socket,在22222端口監聽??????????????????????????????????????????????????????????????????????
???? server.accept(); //等待連接??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? server.close(); //有連接到來,也就是說有新的實例????????????????????????????????????????????????????????????????????????????????????????????????
???? System.exit(0); //這個實例退出??????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????}catch (Exception e)??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????{????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? e.printStackTrace();????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????}????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
?? }??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??}??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
下面這個Java程序的程序入口是沒有實例控制功能的:????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??public class ProgramMain {??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
?? public static void main(String argv[])????????????????????????????????????????????????????????????????????????????????????????????????????????????
?? {??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????mainFrame frame = new mainFrame();????????????????????????????????????????????????????????????????????????????????????????????????????????????????
?? }??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??}??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
現在想加入實例控制,只需要添加兩行代碼,添加后代碼如下所示:????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??public class ProgramMain {??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
?? public static void main(String argv[])????????????????????????????????????????????????????????????????????????????????????????????????????????????
?? {??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????InstanceControl ic = new InstanceControl();??????????????????????????????????????????????????????????????????????????????????????????????????????
????ic.start();??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????mainFrame frame = new mainFrame();????????????????????????????????????????????????????????????????????????????????????????????????????????????????
?? }??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??}??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
在這個基礎上,要實現第二種情況,也就是已經有實例運行的情況下,新的實例退出,保持原有的運行實例,就只需要一點小的改動了。具體的實現如下:????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??import java.net.*;??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??public class InstanceControl2 extends Thread {??????????????????????????????????????????????????????????????????????????????????????????????????????
?? public void run() {????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????try{??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? Socket sock = new Socket("127.0.0.1", 22222);//創建socket,連接22222端口??????????????????????????????????????????????????????????????????????????
???? System.exit(0); //連接成功,說明有實例存在,則退出??????????????????????????????????????????????????????????????????????????????????????????????
????}catch (Exception e)??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????{}????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????try{??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? ServerSocket server = new ServerSocket(22222);//創建socket,連接22222端口????????????????????????????????????????????????????????????????????????
???? while (true)????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? {????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????server.accept(); //接受連接請求????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? }????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????}catch (Exception e)??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????{????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???? e.printStackTrace();????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????}????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
?? }??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??}??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
這個類的使用方法和第一種情況的那個類是一樣的,只需要在原有的代碼上加入兩行代碼即可:????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??InstanceControl2 ic = new InstanceControl();????????????????????????????????????????????????????????????????????????????????????????????????????????
??ic.start();????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
???????????????????????????????????????????????????????????????????????? 擴展????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
上面的程序也許有一個小bug,就是如果程序在開始運行時ServerSocket監聽的端口已經被其它程序占用,那么程序的運行就會受到影響。所以程序的端口應該盡量取得??
大一些,在這種情況下其它程序占用這個程序使用的端口的概率是可以忽略不計的。同時,還可以做兩種擴展,第一種是把端口寫在配置文件中,可通過讀配置文件得到
端口,這樣就能夠在其它程序占用目前端口的情況下改變這個程序使用的端口。還有一種是在運行的時候用兩個InstanceControl類分別在兩個端口監聽,只要有一個????
InstanceControl類得到連接就做出響應,這樣兩個端口都被其它程序占用的概率就更加的微乎其微了。??????????????????????????????????????????????????????????
???????????????????????????????????