許多程序只允許啟動(dòng)單個(gè)實(shí)例,比如我們常用的MSN、OUTLOOK等等。單實(shí)例有很多好處,其中最重要一點(diǎn)即是安全。想像一下多人同時(shí)修改同一文件的危險(xiǎn),就如我們?cè)贘2EE應(yīng)用程序中所做的那樣,要盡可能考慮到多用戶同時(shí)訪問的問題。
前些日子用SWING為朋友寫了一個(gè)程序,其中就有不可同時(shí)啟動(dòng)多個(gè)系統(tǒng)實(shí)例的要求。由于沒有一個(gè)好的思路,所以走了很多彎路。現(xiàn)在終于解決了,積累了一些心德想與大家分享。
初遇該問題時(shí),我首先想到的是進(jìn)程。當(dāng)程序啟動(dòng)時(shí)判斷操作系統(tǒng)中是否存在該進(jìn)程,如果存在就退出啟動(dòng),否則啟動(dòng)程序。這個(gè)方法在VB或C語言中可以通過調(diào)用WIN32?API來實(shí)現(xiàn)。在JAVA中,要想實(shí)現(xiàn)該方法或許還要借助C的力量。(對(duì)于JAVA如何捕獲進(jìn)程,還請(qǐng)批評(píng)指正。)
放棄了第一種方法,想到了弱智的方法--配置文件。當(dāng)系統(tǒng)第一次啟動(dòng)時(shí)將標(biāo)識(shí)設(shè)置為啟動(dòng)中,退出時(shí)將標(biāo)識(shí)設(shè)置為未啟動(dòng)。但很快就發(fā)現(xiàn),當(dāng)非法關(guān)閉程序(比如關(guān)機(jī)時(shí)未及時(shí)關(guān)閉程序)后,我們的程序便永遠(yuǎn)長(zhǎng)眠了。
其實(shí),在該程序中,最限制我們思路的便是"單機(jī)版"這三個(gè)字。它給我們的印象是僅供一臺(tái)機(jī)器單獨(dú)使用,與網(wǎng)絡(luò)無關(guān)的。因此,我們很難將思路整理到服務(wù)器與客戶機(jī)中去。但要解決該問題恰恰要用到服務(wù)器與客戶機(jī)的概念。想像一下我們平時(shí)啟動(dòng)電腦的步驟,首先按下加電,有了電,電腦才能啟動(dòng)。沒錯(cuò),只有當(dāng)電腦未加電,也就是說當(dāng)我們首次啟動(dòng)電腦時(shí)才會(huì)做這個(gè)動(dòng)作,而且這個(gè)動(dòng)作在一段時(shí)間內(nèi)只會(huì)做一次。若要重新加電,就必需先斷電。現(xiàn)在回到我們的程序,有了這個(gè)思路,留給我們的問題就是誰來充當(dāng)電的角色呢?沒錯(cuò),就是之前提到過的服務(wù)器。程序首次啟動(dòng)時(shí)首先連接指定端口的服務(wù)器,發(fā)現(xiàn)服務(wù)器并未啟動(dòng),于是啟動(dòng)服務(wù)器,啟動(dòng)程序。當(dāng)程序復(fù)數(shù)啟動(dòng)時(shí),再次連接服務(wù)器,這時(shí)發(fā)現(xiàn)服務(wù)器已經(jīng)啟動(dòng)了,于是就終止啟動(dòng)。代碼如下:?
public?class?Console?{
??/**端口號(hào)*/
??private?static?int?iPort?=?50000;
??/**主窗口*/
??JFrame?frame?=?null;?
??/**
??*?系統(tǒng)入口
??*?@param?String[]?args
??*?*/
??public?static?void?main(String[]?args)?throws?Exception?{
????Socket?socket?=?null;?//客戶端連接器
????Thread?thread?=?null;?//啟動(dòng)服務(wù)器的線程
????try?{
??????//連接服務(wù)器
??????//如果服務(wù)器未啟動(dòng)則拋異常
??????(socket?=?new?Socket("localhost",?iPort)).close();
??????//如果服務(wù)器已經(jīng)啟動(dòng)則退出系統(tǒng)
??????System.exit(0);
????}?catch?(Exception?e)?{}//未做處理
????//如果服務(wù)器未啟動(dòng)則在新的線程中啟動(dòng)服務(wù)器
????(thread?=?new?Thread(new?Server())).setDaemon(true);
????//開始線程
????thread.start();
????//啟動(dòng)主程序
????frame?=?new?JFrame("學(xué)海無涯");
????frame.setVisible(true);
??}
??/**
??*?端口監(jiān)聽服務(wù)器端運(yùn)行
??*?@author?hiswing
??*/
??static?class?Server?implements?Runnable?{
????public?final?void?run()?{
??????ServerSocket?serversocket?=?null;
??????//查找沒有占用的端口
??????while?(iPort?<?60000)?{
????????try{
??????????serversocket?=?new?ServerSocket(iPort);
????????}catch(Exception?ex){
??????????iPort++;
????????}
????????break;
??????}
??????try?{
????????do?{
??????????//監(jiān)聽客戶端是否有連接
??????????serversocket.accept();
??????????//窗口在任務(wù)欄閃動(dòng)
??????????if(frame.getExtendedState()?==?1)?{
????????????frame.setExtendedState(0);
??????????}
??????????if(frame.getExtendedState()?!=?1)?{
????????????frame.toFront();
????????????frame.requestFocus();
????????????frame.repaint();
??????????}
????????}?while(true);
??????}?catch?(Exception?ex)?{
????????//不做處理
??????}
????}
??}
}
由于沒有好的思路,使我們?cè)诰幊讨凶吡嗽S多彎路,浪費(fèi)了寶貴的時(shí)間。都說軟件是智慧的結(jié)晶,一點(diǎn)沒錯(cuò)。