隨筆 - 18  文章 - 96  trackbacks - 0
          <2007年11月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678


          常用鏈接

          留言簿(4)

          隨筆檔案

          相冊

          我的兄弟們

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

               摘要: 很久沒有回來這里寫技術BLOG了,這里的氛圍還行,大家都對一個問題積極的思考(至少之前這里給我的感覺是這樣的),2年里面自己也忙著做些事情,沒有寫,最近有空也就寫寫,偶爾會去oschine.net看看新聞,然后就在那里看到了一個人提出的問題很有意思,就是怎么表達式求解,例如(1 + 2) / 3 - 1 * 2 + 5 / (3 + 2)這樣的字符串輸入,怎么樣解析之后輸出結果。說來也好笑,對于我...  閱讀全文
          posted @ 2011-11-09 10:36 ruislan 閱讀(1768) | 評論 (6)編輯 收藏
               摘要: 很久沒來了,不是一位朋友給我發郵件問我關于swing的問題,才想起,然后翻看了之前的代碼,發現當年還實現了一個Vista風格的按鈕沒有放出來,現在補上,也許現在人們的swing水平對我這代碼不屑一顧,不過還是依然拋磚引玉,給未知的人們一個啟發。還是老慣例,上效果圖: 正常情況下:(和vista一樣具有焦點的按鈕的顏色漸深漸淺的循環變化) 鼠標在區域內時: 鼠標按下去: 然后是代...  閱讀全文
          posted @ 2009-09-12 12:54 ruislan 閱讀(2496) | 評論 (3)編輯 收藏
          認為自己是達人的就不用看了。只是一點小技巧,不敢班門弄斧,做個總結,為那些還不知道的解解惑,隨便告訴大家我還活著。

          最近客戶提了個小改動,客戶網站上圖片存放的目錄需要改動一下。例如在網上訪問是www.aygfsteel.com/images/*.*,在服務器上的目錄是D:/<webroot>/images/*.*,客戶想把這個images目錄下的資源全部移動到E:/data/里面去,但是在網上www.aygfsteel.com/images/*.*還是同樣可以訪問得到,我剛開始犯了形式主義的錯誤,老是想用程序解決,一會filter,一會servlet/action,后來我配置程序的時候突然看到了server.xml,于是我想到了選擇用映射的方式。正好,server.xml中的<Context>就是做這個事情的。于是乎我們在<Host></Host>中增加了一個<Context docBase="E:/data/images" path="/images">,OK,重啟之后,所有檢索www.aygfsteel.com/images路徑下的資源實際上都由E:/data/images下的資源提供了。

          posted @ 2008-02-15 16:41 ruislan 閱讀(1276) | 評論 (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://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();
                      }
                  }
              }
          }


          乍看之下,我們的代碼沒有問題,但是運行之后你會發現,這句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://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();
                  }
              }
          }


          新的代碼,我們在Service的線程提供了一個待處理Channel隊列,然后在添加一個SocketChannel到隊列中時喚醒這個selector,取消阻塞,然后在Service的循環中處理這個pendingChannel,這樣就避免這個Deadlock的發生了。當然我們亦可以在那個代碼上將select的超時時間設置非常的短,然后讓兩個線程去競爭,這樣做有太多的不可控性,不推薦了。

          posted @ 2007-12-13 18:31 ruislan 閱讀(1355) | 評論 (3)編輯 收藏
              UI作為用戶與電腦的交互界面,如何更好的服務于人,讓人們用起來方便、簡單、快捷一直是UI開發者應該有的覺悟,作為開發人員的我們來說,不應該只是把UI推給電腦平面設計人員,更不應該一手包辦了(如果你不是一個人的話)。我們開發人員常常在開發UI的時候避重就輕,基本上都在強調code的美學,模式的應用而忽略了真實用戶的感受。我們常常得意于自己技術的美麗,而將一些比自己水平低的應用嗤之以鼻。但是用戶卻從來不關心代碼是如何寫的,他們關心這個應用是否對他們有用,順手乎?聰明乎?所以如果我們只是美麗于自己的設計,太關注軟件的本身而忽略了用戶的感受,就跟某些象牙塔里拿著錢做些無用的研究的人沒什么兩樣,或許有個美麗的名詞,為了科學。
              那么如何才能算是好的UI人性化的設計呢?這個得看針對的用戶主要是哪些。我們熟知的操作系統Windows XP,Windows Vista,Vista是微軟最新的操作系統,包含了很多開發人員辛苦的結晶,但是在我身邊的很多人都不愿意裝它,也包括一些新聞的調查也說Vista不如當年XP出世那般火爆,他們大多數不愿裝的都說了同樣的話,XP都還有很多不懂,怕Vista更搞不懂,說實話我用過Vista,就我這么一個算是業內人士用起來當然駕輕就熟,再加上我們都有勇于創新的精神,所以常常去用新的東西,而普通客戶就不這么想了,我問了幾個不懂電腦才安裝了Vista的用戶的感受,“開始菜單的‘開始’兩個字沒有了,我還以為換了位置”,“界面比XP漂亮啊,但是我的機器好像有點慢,是不是要設置個什么啊”……再來我們熟知的AJAX,我已經接到過很多次不同的人給我的電話,說“為什么網頁打開的時候突然好卡了,以前不這樣啊?”,“網頁瀏覽不了,老說請稍候,數據加載中,等了很久,就是不出現”……面對這些電話或許我們會說,你們怎么那么笨啊,它卡是因為在下東西,在執行JS,寫JS的人太垃圾,浪費了資源,不出現就刷新啊,不要瀏覽那個不專業的網站了,等等就OK了等等回答,其實很多時候我們可以避免用戶的問題出現,例如你的AJAX的JS太大的時候,可以先提示用戶說,數據量較大,請稍后,如果長時間無反應,請按瀏覽器的刷新按鈕,或者嘗試按下F5鍵。
              我還見過許多軟件鼓吹自己的功能如何強大,如何厲害,多么的人性化,但是我打開他們的軟件,居然發現只能用鼠標操作!!這是多么大的UI設計失敗!在舉一個例子,MSN和QQ兩個IM,如果你用MSN,在聯系人框里按上下的話,MSN會很聰明的明白你是要選擇上一位或者下一位聯系人,而QQ會很聰明的明白你是要拖動滑動條!@#$,還有很多軟件記憶力太差,不管我如何操作,它就是記不住,關閉軟件重新啟動后又回到了最初的模樣,還有的軟件自信心不足,一再問我“你確定嗎?”,“真的要這樣做嗎?”,“或許您不小心點了?”而我只是在點關閉這個娛樂性質的軟件而已,而有些軟件又特膽肥,做了一個不可恢復的操作盡然連提示都沒有,還有的軟件文化太差,常常把一個按鈕或者圖標該表達的含義弄得模棱兩可,以至于常常讓我們會錯意,做錯操作,或者把一些高風險的操作放在常用操作的旁邊,很容易點錯,還有的把不常用的操作也放到常用操作區,還不告訴用戶怎么去掉,這樣的例子不勝枚舉。
              出現這些問題的原因在于我們與用戶之間的思維方式有著很大的不同。例如在寫文章之前我才將老爸從我的電腦椅上請下來,請下來之前他正在看我吃飯前的網頁——“界面九宮格”,我說您能看得懂嘛,他說“不懂,不過這軟件的界面不都這樣嘛?再說了,一張紙就8個方位,加上中間正好九個,你的東西不擺這里擺哪里啊?”,我正要解釋一下這與軟件設計的關系,但是突然一想,是啊,有道理啊,要是我給他老人家再解釋一下可以放在上面和下面,那不就是3D的了。再比如我一直都很不屑一顧的網絡實名,但是當它被我在很多人的機器里面消滅之后,很多人都打電話問我,怎么在地址欄里面輸入漢字,跳出搜索界面了,不是那個漢字的網站了,以前是有的,原來我只看到了它流氓的一面,忽略了普通用戶是根本記不住網址在哪里,甚至有些用戶不懂英文,你怎么讓他記得住全是英文的網址呢?不過過了幾天,他們都說不用了,有一個網站導航網址做了他們的主頁,他們平日想去的網站都在上面列著的,我后來才知道,就是被我以前同寢室的刪除了半天的hao123。所以我們必須充分考慮我們的應用是針對哪些用戶,他們是哪一類人,習慣是什么,當然還有就是UI設計的一些基本的東西,例如鼠標能夠完成的動作,同樣鍵盤也能完成等等。

          posted @ 2007-11-11 13:36 ruislan 閱讀(1429) | 評論 (5)編輯 收藏
          Swing作為一個完整的UI解決方案,包含了一個GUI程序所擁有的方方面面,當然包括作為普通程序也好,作為GUI程序也好,作為Web程序等等程序都共有的線程概念。

          Swing中的線程有三種:初始線程,事件線程,工作線程

          這三種線程基本上包括了讓一個GUI完美工作的方方面面,首先,初始線程被用來創建GUI組件、資源加載和啟動GUI組件,眾所周知,Swing是事件驅動的,所以當UI出現了之后,初始線程就完成了它的使命,并將接力棒交給了事件線程,Event Dispatch Thread,這個時候所有組件的事件行為都交給了這個線程去處理,當然我們自己也要需要用線程來運行許多任務,優秀的GUI程序是絕不能讓界面被卡死不動的,那會讓用戶崩潰,所以這個時候就需要工作線程了,也可以說是在背后運行的線程,這種線程是勞動階級,任勞任怨的執行者長時間的工作。

          初始線程的寫法很簡單,這樣就可以了:
          SwingUtilities.invokeLater(new Runnable() {
              
          public void run() {
                  initGUI();
                  showGUI();
              }
          }

          但是Applet中,你可能需要調用SwingUtilities.invokeAndWait這個方法,要是init方法返回了,瀏覽器開始展現Applet,但是GUI的創建還在thread中,出錯也是可想而知的。
          至于invokeLater和invokeAndWait這兩個線程的簡單點的區別就是invokeLater是異步的,你不知道它什么時候會開始執行,invokeAndWait則是同步的,它會等到動作執行完成之后才返回。

          Event Dispatch Thread不是線程安全的,所以要用線程來與它打交道要注意了,同步問題總是讓人頭痛。

          在1.5之前應該說工作線程都是由開發人員自己去定義的,但是現在Swing推薦了SwingWorker這個類,包括Swing最新的符合JSR標準的Swing AppFramework也使用了SwingWorker這個類來處理所有在GUI背后做的事情。

          了解了Swing中的線程定義,能夠讓我們更好的寫出優美的基于Swing的GUI程序。

          posted @ 2007-11-04 12:40 ruislan 閱讀(1275) | 評論 (2)編輯 收藏
          主站蜘蛛池模板: 马关县| 阿克苏市| 通渭县| 驻马店市| 丹巴县| 睢宁县| 台前县| 科尔| 扎赉特旗| 桂阳县| 旬邑县| 融水| 石柱| 陵水| 邢台县| 公主岭市| 开封市| 台南县| 云林县| 同德县| 镇坪县| 罗田县| 横峰县| 晋州市| 大埔区| 揭西县| 绍兴市| 泽库县| 大渡口区| 龙泉市| 通江县| 原阳县| 台东县| 上蔡县| 本溪| 镶黄旗| 鄄城县| 文山县| 西贡区| 宜兴市| 遂昌县|