摘要: 很久沒有回來這里寫技術BLOG了,這里的氛圍還行,大家都對一個問題積極的思考(至少之前這里給我的感覺是這樣的),2年里面自己也忙著做些事情,沒有寫,最近有空也就寫寫,偶爾會去oschine.net看看新聞,然后就在那里看到了一個人提出的問題很有意思,就是怎么表達式求解,例如(1 + 2) / 3 - 1 * 2 + 5 / (3 + 2)這樣的字符串輸入,怎么樣解析之后輸出結果。說來也好笑,對于我...
閱讀全文
posted @
2011-11-09 10:36 ruislan 閱讀(1762) |
評論 (6) |
編輯 收藏
摘要: 很久沒來了,不是一位朋友給我發郵件問我關于swing的問題,才想起,然后翻看了之前的代碼,發現當年還實現了一個Vista風格的按鈕沒有放出來,現在補上,也許現在人們的swing水平對我這代碼不屑一顧,不過還是依然拋磚引玉,給未知的人們一個啟發。還是老慣例,上效果圖:
正常情況下:(和vista一樣具有焦點的按鈕的顏色漸深漸淺的循環變化)
鼠標在區域內時:
鼠標按下去:
然后是代...
閱讀全文
posted @
2009-09-12 12:54 ruislan 閱讀(2487) |
評論 (3) |
編輯 收藏
認為自己是達人的就不用看了。只是一點小技巧,不敢班門弄斧,做個總結,為那些還不知道的解解惑,隨便告訴大家我還活著。
最近客戶提了個小改動,客戶網站上圖片存放的目錄需要改動一下。例如在網上訪問是m.tkk7.com/images/*.*,在服務器上的目錄是D:/<webroot>/images/*.*,客戶想把這個images目錄下的資源全部移動到E:/data/里面去,但是在網上m.tkk7.com/images/*.*還是同樣可以訪問得到,我剛開始犯了形式主義的錯誤,老是想用程序解決,一會filter,一會servlet/action,后來我配置程序的時候突然看到了server.xml,于是我想到了選擇用映射的方式。正好,server.xml中的<Context>就是做這個事情的。于是乎我們在<Host></Host>中增加了一個<Context docBase="E:/data/images" path="/images">,OK,重啟之后,所有檢索m.tkk7.com/images路徑下的資源實際上都由E:/data/images下的資源提供了。
posted @
2008-02-15 16:41 ruislan 閱讀(1272) |
評論 (3) |
編輯 收藏
在寫多線程程序的時候,你就像個經理,手下有那么或多或少的職員,你負責協調職員之間的工作,如果你稍不留神,職員之間就陷入了相互等待的尷尬狀態。還好,大多數時候多線程都還在我們掌控之內,即便是遇到這樣的deadlock情況,我們也能夠去修正,但是有的時候生活就是那么不盡人意,特別是NIO這種你不能掌控的時候,且看下面的代碼:
/**
* @(#)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://m.tkk7.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();
}
}
}
}
乍看之下,我們的代碼沒有問題,但是運行之后你會發現,這句System.out.println("can reach here?when pigs fly!");永遠無法執行,也就是說register()方法被阻塞了!Oh god bless,讓我們看看JavaDoc是怎么說的:
...
可在任意時間調用此方法。如果調用此方法的同時正在進行另一個此方法或 configureBlocking 方法的調用,則在另一個操作完成前將首先阻塞該調用。然后此方法將在選擇器的鍵集上實現同步,因此如果調用此方法時并發地調用了涉及同一選擇器的另一個注冊或選擇操作,則可能阻塞此方法的調用。
...
看這句“可在任意時間調用此方法。”,也就是說我們調用的時間沒有任何限制,而阻塞的情況只會出現在“如果調用此方法的同時正在進行另一個此方法或 configureBlocking 方法的調用”的情況下,即便是阻塞了,我相信“正在進行另一個此方法或configureBlocking”也不會花掉太多的時間,況且這里沒有上面這樣的情況出現。那register()是被誰擋住了?或者是BUG?
我們來分析一下程序,程序有兩個線程主線程和Service線程,主線程啟動后啟動了Service線程,Service線程啟動Selector然后Service線程陷入select()的阻塞中,同時,主線程調用Service的addChannel()方法來添加一個SocketChannel,嗯,兩個線程之間唯一的聯系就是selector,看來要從selector尋找線索,很可惜,selector的實現沒有源代碼可查,不過可以肯定是channel的register()會調用selector的register(),雖然此時持有selector的Service線程被select()方法所阻塞,但是并不影響其他線程對其操作吧?那么,剩下的解釋就是Selector的select()方法和register()方法公用了一個鎖,select()方法阻塞住了,所以register()拿不到這個鎖了,那么這樣一來我們就只能保證讓select()或者register()不能同時調用或者register()調用的時候select()不持有這個鎖,也就是說我們要用Service線程自己來執行addChannel()方法,所以改進如下:
/**
* @(#)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://m.tkk7.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();
}
}
}
新的代碼,我們在Service的線程提供了一個待處理Channel隊列,然后在添加一個SocketChannel到隊列中時喚醒這個selector,取消阻塞,然后在Service的循環中處理這個pendingChannel,這樣就避免這個Deadlock的發生了。當然我們亦可以在那個代碼上將select的超時時間設置非常的短,然后讓兩個線程去競爭,這樣做有太多的不可控性,不推薦了。
posted @
2007-12-13 18:31 ruislan 閱讀(1350) |
評論 (3) |
編輯 收藏