摘要: 很久沒(méi)有回來(lái)這里寫(xiě)技術(shù)BLOG了,這里的氛圍還行,大家都對(duì)一個(gè)問(wèn)題積極的思考(至少之前這里給我的感覺(jué)是這樣的),2年里面自己也忙著做些事情,沒(méi)有寫(xiě),最近有空也就寫(xiě)寫(xiě),偶爾會(huì)去oschine.net看看新聞,然后就在那里看到了一個(gè)人提出的問(wèn)題很有意思,就是怎么表達(dá)式求解,例如(1 + 2) / 3 - 1 * 2 + 5 / (3 + 2)這樣的字符串輸入,怎么樣解析之后輸出結(jié)果。說(shuō)來(lái)也好笑,對(duì)于我...
閱讀全文
posted @
2011-11-09 10:36 ruislan 閱讀(1763) |
評(píng)論 (6) |
編輯 收藏
摘要: 很久沒(méi)來(lái)了,不是一位朋友給我發(fā)郵件問(wèn)我關(guān)于swing的問(wèn)題,才想起,然后翻看了之前的代碼,發(fā)現(xiàn)當(dāng)年還實(shí)現(xiàn)了一個(gè)Vista風(fēng)格的按鈕沒(méi)有放出來(lái),現(xiàn)在補(bǔ)上,也許現(xiàn)在人們的swing水平對(duì)我這代碼不屑一顧,不過(guò)還是依然拋磚引玉,給未知的人們一個(gè)啟發(fā)。還是老慣例,上效果圖:
正常情況下:(和vista一樣具有焦點(diǎn)的按鈕的顏色漸深漸淺的循環(huán)變化)
鼠標(biāo)在區(qū)域內(nèi)時(shí):
鼠標(biāo)按下去:
然后是代...
閱讀全文
posted @
2009-09-12 12:54 ruislan 閱讀(2490) |
評(píng)論 (3) |
編輯 收藏
認(rèn)為自己是達(dá)人的就不用看了。只是一點(diǎn)小技巧,不敢班門(mén)弄斧,做個(gè)總結(jié),為那些還不知道的解解惑,隨便告訴大家我還活著。
最近客戶(hù)提了個(gè)小改動(dòng),客戶(hù)網(wǎng)站上圖片存放的目錄需要改動(dòng)一下。例如在網(wǎng)上訪問(wèn)是www.aygfsteel.com/images/*.*,在服務(wù)器上的目錄是D:/<webroot>/images/*.*,客戶(hù)想把這個(gè)images目錄下的資源全部移動(dòng)到E:/data/里面去,但是在網(wǎng)上www.aygfsteel.com/images/*.*還是同樣可以訪問(wèn)得到,我剛開(kāi)始犯了形式主義的錯(cuò)誤,老是想用程序解決,一會(huì)filter,一會(huì)servlet/action,后來(lái)我配置程序的時(shí)候突然看到了server.xml,于是我想到了選擇用映射的方式。正好,server.xml中的<Context>就是做這個(gè)事情的。于是乎我們?cè)?lt;Host></Host>中增加了一個(gè)<Context docBase="E:/data/images" path="/images">,OK,重啟之后,所有檢索www.aygfsteel.com/images路徑下的資源實(shí)際上都由E:/data/images下的資源提供了。
posted @
2008-02-15 16:41 ruislan 閱讀(1273) |
評(píng)論 (3) |
編輯 收藏
在寫(xiě)多線程程序的時(shí)候,你就像個(gè)經(jīng)理,手下有那么或多或少的職員,你負(fù)責(zé)協(xié)調(diào)職員之間的工作,如果你稍不留神,職員之間就陷入了相互等待的尷尬狀態(tài)。還好,大多數(shù)時(shí)候多線程都還在我們掌控之內(nèi),即便是遇到這樣的deadlock情況,我們也能夠去修正,但是有的時(shí)候生活就是那么不盡人意,特別是NIO這種你不能掌控的時(shí)候,且看下面的代碼:
/**
* @(#)DeadLock.java v0.1.0 2007-12-13
*/
package ruislan.rswing.test;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executors;
/**
* NIO DeadLock
*
* @author ruislan <a href="mailto:z17520@126.com"/>
* @version 0.1.0
*/
public class DeadLock {
public static void main(String[] args) throws Exception {
Service service = new Service();
Executors.newSingleThreadExecutor().execute(service);
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("http://www.aygfsteel.com", 80));
service.addChannel(channel);
}
static class Service implements Runnable {
Selector selector;
public Service() {
}
public void run() {
try {
selector = Selector.open();
while (true) {
selector.select();
System.out.println(selector.selectedKeys().size());
}
} catch (Exception e) {
}
}
public void addChannel(SocketChannel channel) {
try {
channel.register(selector, SelectionKey.OP_CONNECT
| SelectionKey.OP_READ);
System.out.println("can reach here?when pigs fly!");
} catch (ClosedChannelException e) {
e.printStackTrace();
}
}
}
}
乍看之下,我們的代碼沒(méi)有問(wèn)題,但是運(yùn)行之后你會(huì)發(fā)現(xiàn),這句System.out.println("can reach here?when pigs fly!");永遠(yuǎn)無(wú)法執(zhí)行,也就是說(shuō)register()方法被阻塞了!Oh god bless,讓我們看看JavaDoc是怎么說(shuō)的:
...
可在任意時(shí)間調(diào)用此方法。如果調(diào)用此方法的同時(shí)正在進(jìn)行另一個(gè)此方法或 configureBlocking 方法的調(diào)用,則在另一個(gè)操作完成前將首先阻塞該調(diào)用。然后此方法將在選擇器的鍵集上實(shí)現(xiàn)同步,因此如果調(diào)用此方法時(shí)并發(fā)地調(diào)用了涉及同一選擇器的另一個(gè)注冊(cè)或選擇操作,則可能阻塞此方法的調(diào)用。
...
看這句“可在任意時(shí)間調(diào)用此方法。”,也就是說(shuō)我們調(diào)用的時(shí)間沒(méi)有任何限制,而阻塞的情況只會(huì)出現(xiàn)在“如果調(diào)用此方法的同時(shí)正在進(jìn)行另一個(gè)此方法或 configureBlocking 方法的調(diào)用”的情況下,即便是阻塞了,我相信“正在進(jìn)行另一個(gè)此方法或configureBlocking”也不會(huì)花掉太多的時(shí)間,況且這里沒(méi)有上面這樣的情況出現(xiàn)。那register()是被誰(shuí)擋住了?或者是BUG?
我們來(lái)分析一下程序,程序有兩個(gè)線程主線程和Service線程,主線程啟動(dòng)后啟動(dòng)了Service線程,Service線程啟動(dòng)Selector然后Service線程陷入select()的阻塞中,同時(shí),主線程調(diào)用Service的addChannel()方法來(lái)添加一個(gè)SocketChannel,嗯,兩個(gè)線程之間唯一的聯(lián)系就是selector,看來(lái)要從selector尋找線索,很可惜,selector的實(shí)現(xiàn)沒(méi)有源代碼可查,不過(guò)可以肯定是channel的register()會(huì)調(diào)用selector的register(),雖然此時(shí)持有selector的Service線程被select()方法所阻塞,但是并不影響其他線程對(duì)其操作吧?那么,剩下的解釋就是Selector的select()方法和register()方法公用了一個(gè)鎖,select()方法阻塞住了,所以register()拿不到這個(gè)鎖了,那么這樣一來(lái)我們就只能保證讓select()或者register()不能同時(shí)調(diào)用或者register()調(diào)用的時(shí)候select()不持有這個(gè)鎖,也就是說(shuō)我們要用Service線程自己來(lái)執(zhí)行addChannel()方法,所以改進(jìn)如下:
/**
* @(#)DeadLock.java v0.1.0 2007-12-13
*/
package ruislan.rswing.test;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* NIO DeadLock
*
* @author ruislan <a href="mailto:z17520@126.com"/>
* @version 0.1.0
*/
public class DeadLock {
public static void main(String[] args) {
Service service = new Service();
new Thread(service).start();
for (int i = 0; i < 5; i++) {
new Thread(new ChannelAdder(service)).start();
}
}
static class ChannelAdder implements Runnable {
private Service service;
public ChannelAdder(Service service) {
this.service = service;
}
@Override
public void run() {
try {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress(
"http://www.aygfsteel.com", 80));
service.addChannel(channel);
} catch (Exception e) {
e.printStackTrace();
}
}
}
static class Service implements Runnable {
private Selector selector;
private Queue<SocketChannel> pendingRegisters;
public Service() {
pendingRegisters = new LinkedBlockingQueue<SocketChannel>();
}
public void run() {
try {
selector = Selector.open();
while (true) {
selector.select();
System.out.println(selector.selectedKeys().size());
handlePendingRegisters();
}
} catch (Exception e) {
}
}
public void handlePendingRegisters() {
while (!pendingRegisters.isEmpty()) {
SocketChannel channel = pendingRegisters.poll();
try {
channel.register(selector, SelectionKey.OP_CONNECT);
System.out.println("can reach here?yeah!");
} catch (ClosedChannelException e) {
e.printStackTrace();
}
}
}
public void addChannel(SocketChannel channel) {
pendingRegisters.offer(channel);
selector.wakeup();
}
}
}
新的代碼,我們?cè)赟ervice的線程提供了一個(gè)待處理Channel隊(duì)列,然后在添加一個(gè)SocketChannel到隊(duì)列中時(shí)喚醒這個(gè)selector,取消阻塞,然后在Service的循環(huán)中處理這個(gè)pendingChannel,這樣就避免這個(gè)Deadlock的發(fā)生了。當(dāng)然我們亦可以在那個(gè)代碼上將select的超時(shí)時(shí)間設(shè)置非常的短,然后讓兩個(gè)線程去競(jìng)爭(zhēng),這樣做有太多的不可控性,不推薦了。
posted @
2007-12-13 18:31 ruislan 閱讀(1352) |
評(píng)論 (3) |
編輯 收藏
UI作為用戶(hù)與電腦的交互界面,如何更好的服務(wù)于人,讓人們用起來(lái)方便、簡(jiǎn)單、快捷一直是UI開(kāi)發(fā)者應(yīng)該有的覺(jué)悟,作為開(kāi)發(fā)人員的我們來(lái)說(shuō),不應(yīng)該只是把UI推給電腦平面設(shè)計(jì)人員,更不應(yīng)該一手包辦了(如果你不是一個(gè)人的話(huà))。我們開(kāi)發(fā)人員常常在開(kāi)發(fā)UI的時(shí)候避重就輕,基本上都在強(qiáng)調(diào)code的美學(xué),模式的應(yīng)用而忽略了真實(shí)用戶(hù)的感受。我們常常得意于自己技術(shù)的美麗,而將一些比自己水平低的應(yīng)用嗤之以鼻。但是用戶(hù)卻從來(lái)不關(guān)心代碼是如何寫(xiě)的,他們關(guān)心這個(gè)應(yīng)用是否對(duì)他們有用,順手乎?聰明乎?所以如果我們只是美麗于自己的設(shè)計(jì),太關(guān)注軟件的本身而忽略了用戶(hù)的感受,就跟某些象牙塔里拿著錢(qián)做些無(wú)用的研究的人沒(méi)什么兩樣,或許有個(gè)美麗的名詞,為了科學(xué)。
那么如何才能算是好的UI人性化的設(shè)計(jì)呢?這個(gè)得看針對(duì)的用戶(hù)主要是哪些。我們熟知的操作系統(tǒng)Windows XP,Windows Vista,Vista是微軟最新的操作系統(tǒng),包含了很多開(kāi)發(fā)人員辛苦的結(jié)晶,但是在我身邊的很多人都不愿意裝它,也包括一些新聞的調(diào)查也說(shuō)Vista不如當(dāng)年XP出世那般火爆,他們大多數(shù)不愿裝的都說(shuō)了同樣的話(huà),XP都還有很多不懂,怕Vista更搞不懂,說(shuō)實(shí)話(huà)我用過(guò)Vista,就我這么一個(gè)算是業(yè)內(nèi)人士用起來(lái)當(dāng)然駕輕就熟,再加上我們都有勇于創(chuàng)新的精神,所以常常去用新的東西,而普通客戶(hù)就不這么想了,我問(wèn)了幾個(gè)不懂電腦才安裝了Vista的用戶(hù)的感受,“開(kāi)始菜單的‘開(kāi)始’兩個(gè)字沒(méi)有了,我還以為換了位置”,“界面比XP漂亮啊,但是我的機(jī)器好像有點(diǎn)慢,是不是要設(shè)置個(gè)什么啊”……再來(lái)我們熟知的AJAX,我已經(jīng)接到過(guò)很多次不同的人給我的電話(huà),說(shuō)“為什么網(wǎng)頁(yè)打開(kāi)的時(shí)候突然好卡了,以前不這樣啊?”,“網(wǎng)頁(yè)瀏覽不了,老說(shuō)請(qǐng)稍候,數(shù)據(jù)加載中,等了很久,就是不出現(xiàn)”……面對(duì)這些電話(huà)或許我們會(huì)說(shuō),你們?cè)趺茨敲幢堪。ㄊ且驗(yàn)樵谙聳|西,在執(zhí)行JS,寫(xiě)JS的人太垃圾,浪費(fèi)了資源,不出現(xiàn)就刷新啊,不要瀏覽那個(gè)不專(zhuān)業(yè)的網(wǎng)站了,等等就OK了等等回答,其實(shí)很多時(shí)候我們可以避免用戶(hù)的問(wèn)題出現(xiàn),例如你的AJAX的JS太大的時(shí)候,可以先提示用戶(hù)說(shuō),數(shù)據(jù)量較大,請(qǐng)稍后,如果長(zhǎng)時(shí)間無(wú)反應(yīng),請(qǐng)按瀏覽器的刷新按鈕,或者嘗試按下F5鍵。
我還見(jiàn)過(guò)許多軟件鼓吹自己的功能如何強(qiáng)大,如何厲害,多么的人性化,但是我打開(kāi)他們的軟件,居然發(fā)現(xiàn)只能用鼠標(biāo)操作!!這是多么大的UI設(shè)計(jì)失敗!在舉一個(gè)例子,MSN和QQ兩個(gè)IM,如果你用MSN,在聯(lián)系人框里按上下的話(huà),MSN會(huì)很聰明的明白你是要選擇上一位或者下一位聯(lián)系人,而QQ會(huì)很聰明的明白你是要拖動(dòng)滑動(dòng)條!@#$,還有很多軟件記憶力太差,不管我如何操作,它就是記不住,關(guān)閉軟件重新啟動(dòng)后又回到了最初的模樣,還有的軟件自信心不足,一再問(wèn)我“你確定嗎?”,“真的要這樣做嗎?”,“或許您不小心點(diǎn)了?”而我只是在點(diǎn)關(guān)閉這個(gè)娛樂(lè)性質(zhì)的軟件而已,而有些軟件又特膽肥,做了一個(gè)不可恢復(fù)的操作盡然連提示都沒(méi)有,還有的軟件文化太差,常常把一個(gè)按鈕或者圖標(biāo)該表達(dá)的含義弄得模棱兩可,以至于常常讓我們會(huì)錯(cuò)意,做錯(cuò)操作,或者把一些高風(fēng)險(xiǎn)的操作放在常用操作的旁邊,很容易點(diǎn)錯(cuò),還有的把不常用的操作也放到常用操作區(qū),還不告訴用戶(hù)怎么去掉,這樣的例子不勝枚舉。
出現(xiàn)這些問(wèn)題的原因在于我們與用戶(hù)之間的思維方式有著很大的不同。例如在寫(xiě)文章之前我才將老爸從我的電腦椅上請(qǐng)下來(lái),請(qǐng)下來(lái)之前他正在看我吃飯前的網(wǎng)頁(yè)——“界面九宮格”,我說(shuō)您能看得懂嘛,他說(shuō)“不懂,不過(guò)這軟件的界面不都這樣嘛?再說(shuō)了,一張紙就8個(gè)方位,加上中間正好九個(gè),你的東西不擺這里擺哪里啊?”,我正要解釋一下這與軟件設(shè)計(jì)的關(guān)系,但是突然一想,是啊,有道理啊,要是我給他老人家再解釋一下可以放在上面和下面,那不就是3D的了。再比如我一直都很不屑一顧的網(wǎng)絡(luò)實(shí)名,但是當(dāng)它被我在很多人的機(jī)器里面消滅之后,很多人都打電話(huà)問(wèn)我,怎么在地址欄里面輸入漢字,跳出搜索界面了,不是那個(gè)漢字的網(wǎng)站了,以前是有的,原來(lái)我只看到了它流氓的一面,忽略了普通用戶(hù)是根本記不住網(wǎng)址在哪里,甚至有些用戶(hù)不懂英文,你怎么讓他記得住全是英文的網(wǎng)址呢?不過(guò)過(guò)了幾天,他們都說(shuō)不用了,有一個(gè)網(wǎng)站導(dǎo)航網(wǎng)址做了他們的主頁(yè),他們平日想去的網(wǎng)站都在上面列著的,我后來(lái)才知道,就是被我以前同寢室的刪除了半天的hao123。所以我們必須充分考慮我們的應(yīng)用是針對(duì)哪些用戶(hù),他們是哪一類(lèi)人,習(xí)慣是什么,當(dāng)然還有就是UI設(shè)計(jì)的一些基本的東西,例如鼠標(biāo)能夠完成的動(dòng)作,同樣鍵盤(pán)也能完成等等。
posted @
2007-11-11 13:36 ruislan 閱讀(1427) |
評(píng)論 (5) |
編輯 收藏