新的起點(diǎn) 新的開始

          快樂生活 !

          深入淺出Java多線程(2)-Swing中的EDT(事件分發(fā)線程)

          接深入淺出Java多線程系列(1),本文主要解決的問(wèn)題是:
          如何使其Swing程序只能運(yùn)行一個(gè)實(shí)例?
          拋開Swing, 我們的程序是通過(guò)java 命令行啟動(dòng)一個(gè)進(jìn)程來(lái)執(zhí)行的,該問(wèn)題也就是說(shuō)要保證這個(gè)進(jìn)程的唯一性,當(dāng)然如果能夠訪問(wèn)系統(tǒng)的接口,得到進(jìn)程的信息來(lái)判斷是否已有進(jìn)程正在運(yùn)行,不就解決了嗎?但是如何訪問(wèn)系統(tǒng)的接口呢?如何要保證在不同的平臺(tái)上都是OK的呢?我的思路是用文件鎖,當(dāng)然我相信肯定有更好的方法,呵呵,希望讀者能夠指出。
          文件鎖是JDK1.4 NIO提出的,可以在讀取一個(gè)文件時(shí),獲得文件鎖,這個(gè)鎖應(yīng)該是系統(tǒng)維護(hù)的,JVM應(yīng)該是調(diào)用的系統(tǒng)文件鎖機(jī)制,例子如下:
          import java.io.FileNotFoundException;
          import java.io.IOException;
          import java.io.RandomAccessFile;
          import java.nio.channels.FileChannel;
          import java.nio.channels.FileLock;
          /**
           *
           * 
          @author vma
           
          */
          public class temp1 {
            
          public static void main(String args[]) throws FileNotFoundException, InterruptedException, IOException{
              RandomAccessFile r 
          = new RandomAccessFile("d://testData.java","rw");
              FileChannel temp 
          = r.getChannel();
              FileLock fl 
          = temp.lock();
              System.out.println(fl.isValid());
              Thread.sleep(
          100000);
              temp.close();
            }
          當(dāng)代碼獲得鎖后:我們?cè)噲D編輯這個(gè)文件是就會(huì):


          如果在啟動(dòng)一個(gè)Java Main方法時(shí):
          public class temp2 {
            
          public static void main(String args[]) throws FileNotFoundException, InterruptedException, IOException{
              RandomAccessFile r 
          = new RandomAccessFile("d://testData.java","rw");
              FileChannel temp 
          = r.getChannel();
              FileLock fl 
          = temp.tryLock();
              System.out.println(fl
          == null);
              temp.close();。
          返回的結(jié)束是 ture , 也就是得不到文件的鎖。

          這就是對(duì)于進(jìn)程唯一性問(wèn)題我的解決思路,通過(guò)鎖定文件使其再啟動(dòng)時(shí)得不到鎖文件而無(wú)法啟動(dòng)。
          說(shuō)到這里,跟今天Swing中的EDT好像還沒有關(guān)系,對(duì)于Swing程序,Main方法中一般像這樣:
            public static void main(String[] args) {
              
          try {
                UIManager.setLookAndFeel(UIManager
                    .getCrossPlatformLookAndFeelClassName());
              } 
          catch (Exception e) {
              }

              
          //Create the top-level container and add contents to it.
              JFrame frame = new JFrame("SwingApplication");
              SwingApplication app 
          = new SwingApplication();
              Component contents 
          = app.createComponents();
              frame.getContentPane().add(contents, BorderLayout.CENTER);

              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.pack();
              frame.setVisible(
          true);
              啟動(dòng)Jframe后,Main線程就退出了,上面獲得文件鎖,并持有鎖的邏輯往哪里寫呢? 有人會(huì)說(shuō)事件分發(fā)線程EDT,真的嗎?
              由于我沒有做過(guò)Swing的項(xiàng)目,僅僅做過(guò)個(gè)人用的財(cái)務(wù)管理小軟件,還沒有深入理解過(guò)EDT,不管怎么說(shuō)先把那段邏輯加到EDT,
              怎么加呢 用SwingUtilities
          static void invokeAndWait(Runnable doRun)
                    Causes doRun.run() to be executed synchronously on the AWT event dispatching thread.
          static void invokeLater(Runnable doRun)
                    Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread.
              加上去以后怎么界面沒有任何反應(yīng)了呢?
          代碼如下:
          package desktopapplication1;
          import java.awt.BorderLayout;
          import java.awt.Component;
          import java.awt.GridLayout;
          import java.awt.event.ActionEvent;
          import java.awt.event.ActionListener;
          import java.awt.event.KeyEvent;

          import java.io.FileNotFoundException;
          import java.io.IOException;
          import java.io.RandomAccessFile;
          import java.lang.reflect.InvocationTargetException;
          import java.nio.channels.FileChannel;
          import java.nio.channels.FileLock;
          import java.util.logging.Level;
          import java.util.logging.Logger;
          import javax.swing.BorderFactory;
          import javax.swing.JButton;
          import javax.swing.JFrame;
          import javax.swing.JLabel;
          import javax.swing.JPanel;
          import javax.swing.SwingUtilities;
          import javax.swing.UIManager;

          public class SwingApplication {
            
          private static String labelPrefix = "Number of button clicks: ";

            
          private int numClicks = 0;

            
          public Component createComponents() {
              
          final JLabel label = new JLabel(labelPrefix + "0    ");

              JButton button 
          = new JButton("I'm a Swing button!");
              button.setMnemonic(KeyEvent.VK_I);
              button.addActionListener(
          new ActionListener() {
                
          public void actionPerformed(ActionEvent e) {
                  numClicks
          ++;
                  label.setText(labelPrefix 
          + numClicks);
                }
              });
              label.setLabelFor(button);

              
          /*
               * An easy way to put space between a top-level container and its
               * contents is to put the contents in a JPanel that has an "empty"
               * border.
               
          */
              JPanel pane 
          = new JPanel();
              pane.setBorder(BorderFactory.createEmptyBorder(
          30//top
                  30//left
                  10//bottom
                  30//right
                  );
              pane.setLayout(
          new GridLayout(01));
              pane.add(button);
              pane.add(label);

              
          return pane;
            }

            
          public static void main(String[] args) throws InterruptedException {
              
          try {
                UIManager.setLookAndFeel(UIManager
                    .getCrossPlatformLookAndFeelClassName());
              } 
          catch (Exception e) {
              }

              
          //Create the top-level container and add contents to it.
              JFrame frame = new JFrame("SwingApplication");
              SwingApplication app 
          = new SwingApplication();
              Component contents 
          = app.createComponents();
              frame.getContentPane().add(contents, BorderLayout.CENTER);

              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.pack();
              frame.setVisible(
          true);
                  
          try {
                      SwingUtilities.invokeAndWait(
          new getFileLock());
                  } 
          catch (InvocationTargetException ex) {
                    ex.printStackTrace();
                  }
            }
            
          }
          class getFileLock implements Runnable{

              
          public void run() {
                  
          try {
                      RandomAccessFile r 
          = null;
                   
          try {
                          r 
          = new RandomAccessFile("d://testData.java""rw");
                      } 
          catch (FileNotFoundException ex) {
                        ex.printStackTrace();
                      }
                      FileChannel temp 
          = r.getChannel();
                      FileLock fl 
          = null;
                      
          try {
                          fl 
          = temp.lock();
                      } 
          catch (IOException ex) {
                          Logger.getLogger(getFileLock.
          class.getName()).log(Level.SEVERE, null, ex);
                      }
              
                      System.out.println(fl.isValid());
                      
          try {
                          Thread.sleep(Integer.MAX_VALUE);
                      } 
          catch (InterruptedException ex) {
                         ex.printStackTrace();
                      }
                      temp.close();
                  } 
          catch (IOException ex) {
                     ex.printStackTrace();
                  }
              }
          }
          打個(gè)斷點(diǎn)看看怎么了,斷點(diǎn)就在這里     Thread.sleep(Integer.MAX_VALUE); 看看那個(gè)線程暫停了 看圖片:



          看到了吧,我們寫的那個(gè)getFileLock 是由AWT-EventQueue-0  線程執(zhí)行,看右下角調(diào)用關(guān)系, EventDispathThread 啟動(dòng) Run方法, 然后pumpEvents 取事件,然后從EventQueue取到InvocationEvent 執(zhí)行Dispath
          Dispath調(diào)用的就是我們?cè)?span style="color: #000000;">getFileLock寫的run() 方法, JDK代碼如下:
            public void dispatch() {
              
          if (catchExceptions) {
                  
          try {
                  runnable.run();
                  } 
                  
          catch (Throwable t) {
                          
          if (t instanceof Exception) {
                              exception 
          = (Exception) t;
                          }
                          throwable 
          = t;
                  }
              }
              
          else {
                  runnable.run();
              }

              
          if (notifier != null) {
                  
          synchronized (notifier) {
                  notifier.notifyAll();
                  }
              }
              }
            runnable.run();
          而如何將我們寫的getFileLock加入的那個(gè)EventQueue中的呢?當(dāng)然是SwingUtilities.invokeAndWait(new getFileLock());
          看JDK代碼:
           public static void invokeAndWait(Runnable runnable)
                       
          throws InterruptedException, InvocationTargetException {

                  
          if (EventQueue.isDispatchThread()) {
                      
          throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
                  }

              
          class AWTInvocationLock {}
                  Object lock 
          = new AWTInvocationLock();

                  InvocationEvent event 
          = 
                      
          new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
                          
          true);

                  
          synchronized (lock) {
                     
          Toolkit.getEventQueue().postEvent(event);
                      lock.wait();
                  }
          Toolkit.getEventQueue().postEvent(event);把我們寫的getFileLock 塞進(jìn)了EventQueue.
          這下讀者對(duì)EDT有個(gè)認(rèn)識(shí)了吧。
          1. EDT 只有一個(gè)線程, 雖然getFileLock是實(shí)現(xiàn)Runnable接口,它調(diào)用的時(shí)候不是star方法啟動(dòng)新線程,而是直接調(diào)用run方法。
          2.
          invokeAndWait將你寫的getFileLock塞到EventQueue中。
          3.
          Swing 事件機(jī)制采用Product Consumer模式 EDT不斷的取EventQueue中的事件執(zhí)行(消費(fèi)者)。其他線程可以將事件塞入EventQueue中,比如鼠標(biāo)點(diǎn)擊Button是,將注冊(cè)在BUttion的事件塞入EventQueue中

          所以我們將
          getFileLock作為事件插入進(jìn)去后 EDT分發(fā)是調(diào)用Thread.sleep(Integer.MAX_VALUE)就睡覺了,無(wú)暇管塞入EventQueue的其他事件了,比如關(guān)閉窗體。

          所以絕對(duì)不能將持有鎖的邏輯塞到EventQueue,而應(yīng)該放到外邊main線程或者其他線程里面。
          提到invokeAndWait,還必須說(shuō)說(shuō)invokelater 這兩個(gè)區(qū)別在哪里呢?
          invokeAndWait與invokelater區(qū)別: 看JDK代碼:

           public static void invokeLater(Runnable runnable) {
                  Toolkit.getEventQueue().postEvent(
                      
          new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));
              }

           public static void invokeAndWait(Runnable runnable)
                       
          throws InterruptedException, InvocationTargetException {

                  
          if (EventQueue.isDispatchThread()) {
                      
          throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
                  }

              
          class AWTInvocationLock {}
                  Object lock 
          = new AWTInvocationLock();

                  InvocationEvent event 
          = 
                      
          new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
                          
          true);

                  
          synchronized (lock) {
                      Toolkit.getEventQueue().postEvent(event);
                      lock.wait();
                  }

                  Throwable eventThrowable 
          = event.getThrowable();
                  
          if (eventThrowable != null) {
                      
          throw new InvocationTargetException(eventThrowable);
                  }
              }
          invokelater:當(dāng)在main方法中調(diào)用SwingUtils.invokelater,后,把事件塞入EventQueue就返回了,main線程不會(huì)阻塞。
          invokeAndWait: 當(dāng)在Main方法中調(diào)用SwingUtils.invokeAndWait 后,看代碼片段:
                  synchronized (lock) {
                      Toolkit.getEventQueue().postEvent(event);
                      lock.wait();
                  }

          main線程獲得lock 后就wait()了,直到事件分發(fā)線程調(diào)用lock對(duì)象的notify喚醒main線程,否則main 就干等著吧。

          這下明白了吧!
          總之,對(duì)于我們問(wèn)題最簡(jiǎn)單的方法就是是main線程里,或者在其他線程里處理。
          最后的解決方案是:
          package desktopapplication1;
          import java.awt.BorderLayout;
          import java.awt.Component;
          import java.awt.GridLayout;
          import java.awt.event.ActionEvent;
          import java.awt.event.ActionListener;
          import java.awt.event.KeyEvent;

          import java.io.FileNotFoundException;
          import java.io.IOException;
          import java.io.RandomAccessFile;
          import java.nio.channels.FileChannel;
          import java.nio.channels.FileLock;

          import javax.swing.BorderFactory;
          import javax.swing.JButton;
          import javax.swing.JFrame;
          import javax.swing.JLabel;
          import javax.swing.JPanel;
          import javax.swing.UIManager;

          public class SwingApplication {
            
          private static String labelPrefix = "Number of button clicks: ";

            
          private int numClicks = 0;

            
          public Component createComponents() {
              
          final JLabel label = new JLabel(labelPrefix + "0    ");

              JButton button 
          = new JButton("I'm a Swing button!");
              button.setMnemonic(KeyEvent.VK_I);
              button.addActionListener(
          new ActionListener() {
                
          public void actionPerformed(ActionEvent e) {
                  numClicks
          ++;
                  label.setText(labelPrefix 
          + numClicks);
                }
              });
              label.setLabelFor(button);

              
          /*
               * An easy way to put space between a top-level container and its
               * contents is to put the contents in a JPanel that has an "empty"
               * border.
               
          */
              JPanel pane 
          = new JPanel();
              pane.setBorder(BorderFactory.createEmptyBorder(
          30//top
                  30//left
                  10//bottom
                  30//right
                  );
              pane.setLayout(
          new GridLayout(01));
              pane.add(button);
              pane.add(label);

              
          return pane;
            }

            
          public static void main(String[] args) throws InterruptedException {
              
          try {
                UIManager.setLookAndFeel(UIManager
                    .getCrossPlatformLookAndFeelClassName());
              } 
          catch (Exception e) {
              }
              Thread t = new Thread(new getFileLock()); 
              t.setDaemon(
          true);
              t.start();

              
          //Create the top-level container and add contents to it.
              JFrame frame = new JFrame("SwingApplication");
              SwingApplication app 
          = new SwingApplication();
              Component contents 
          = app.createComponents();
              frame.getContentPane().add(contents, BorderLayout.CENTER);

              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.pack();
              frame.setVisible(
          true);

            }
            
          }
          class getFileLock implements Runnable{
           

              
          public void run() {
                  
          try {
                      RandomAccessFile r 
          = null;
                   
          try {
                          r 
          = new RandomAccessFile("d://testData.java""rw");
                      } 
          catch (FileNotFoundException ex) {
                        ex.printStackTrace();
                      }
                      FileChannel temp 
          = r.getChannel();
                   
                      
          try {
            
                        FileLock fl = temp.tryLock();
                        
          if(fl == null) System.exit(1);

                        
                      } 
          catch (IOException ex) {
                     ex.printStackTrace();
                      }
                      
          try {
                          Thread.sleep(Integer.MAX_VALUE);
                      } 
          catch (InterruptedException ex) {
                         ex.printStackTrace();
                      }
                      temp.close();
                  } 
          catch (IOException ex) {
                     ex.printStackTrace();
                  }
              }
          }
          在Main方法里啟動(dòng)一個(gè)Daemon線程,持有鎖,如果拿不到鎖,就退出 if(fl == null) System.exit(1);
          當(dāng)然這只是個(gè)解決方案,如何友好給給用戶提示以及鎖定那個(gè)文件就要根據(jù)具體情況而定了。

          posted on 2008-08-24 02:32 advincenting 閱讀(4631) 評(píng)論(4)  編輯  收藏

          評(píng)論

          # re: 深入淺出Java多線程(2)-Swing中的EDT(事件分發(fā)線程) 2008-08-25 23:02 Matthew Chen

          恩,EDT是這樣的,invokeXXX就有點(diǎn)像SWT里面的Display.synXXX,具體名字記不得了,trylock比lock好,是馬上返回而非阻塞吧。  回復(fù)  更多評(píng)論   

          # re: 深入淺出Java多線程(2)-Swing中的EDT(事件分發(fā)線程) 2008-09-01 10:05 w

          1."所以絕對(duì)不能將持有鎖的邏輯塞到EventQueue,而應(yīng)該放到外邊main線程或者其他線程里面。"
          (應(yīng)該是Integer.MAX_VALUE時(shí)間內(nèi)sleep)的邏輯不能放到EDT里吧?在程序的整個(gè)運(yùn)行期間內(nèi)都持有鎖是沒問(wèn)題的
          獲得鎖之前沒有阻塞住后續(xù)代碼的執(zhí)行,也不合適。如果鎖獲取不成功,后續(xù)代碼的執(zhí)行是沒有意義的,我覺得還得用invokeAndWait
          2.是什么意思?Integer.MAX_VALUE毫秒后釋放鎖(2147483647/(1000*60*60*24)≈24.9天)
          假定第一個(gè)程序運(yùn)行24.9(這個(gè)時(shí)間對(duì)程序運(yùn)行來(lái)說(shuō)并不長(zhǎng))天之后,第二個(gè)就可以順利啟動(dòng)了,這就違背了單實(shí)例的本意了
          其他:用socket機(jī)制實(shí)現(xiàn)單實(shí)例運(yùn)行也是一個(gè)不錯(cuò)的方法====================================================
          不知道我理解的對(duì)不對(duì),錯(cuò)誤指出請(qǐng)指正  回復(fù)  更多評(píng)論   

          # re: 深入淺出Java多線程(2)-Swing中的EDT(事件分發(fā)線程) 2008-09-02 21:09 advincenting

          1. 看來(lái)你還是沒有理解 invokeAndWait含義,那會(huì)阻塞調(diào)用該方法的線程,對(duì)于例子就是Main.
          2. 呵呵 如果 Integer.MAX_VALUE不夠 可以用Long.MAX_VALUE啊. 當(dāng)然Socket也可以 但跟你環(huán)境有很大關(guān)系,況且無(wú)緣無(wú)故啟動(dòng)一個(gè)Socket 端口,占用資源不說(shuō),就殺毒軟件都把你滅了。原理上當(dāng)然可以!  回復(fù)  更多評(píng)論   

          # re: 深入淺出Java多線程(2)-Swing中的EDT(事件分發(fā)線程) 2008-09-03 22:01 w

          @advincenting
          用invokeAndWait獲得鎖,在執(zhí)行后續(xù)的程序,有什么問(wèn)題?  回復(fù)  更多評(píng)論   


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           

          公告

          Locations of visitors to this pageBlogJava
        1. 首頁(yè)
        2. 新隨筆
        3. 聯(lián)系
        4. 聚合
        5. 管理
        6. <2008年8月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          統(tǒng)計(jì)

          常用鏈接

          留言簿(13)

          隨筆分類(71)

          隨筆檔案(179)

          文章檔案(13)

          新聞分類

          IT人的英語(yǔ)學(xué)習(xí)網(wǎng)站

          JAVA站點(diǎn)

          優(yōu)秀個(gè)人博客鏈接

          官網(wǎng)學(xué)習(xí)站點(diǎn)

          生活工作站點(diǎn)

          最新隨筆

          搜索

          積分與排名

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 舒城县| 金华市| 杭锦后旗| 夏津县| 余姚市| 确山县| 星子县| 泸西县| 和硕县| 建平县| 鄢陵县| 滕州市| 郓城县| 柳江县| 南郑县| 陆河县| 鄂伦春自治旗| 阿图什市| 福海县| 旺苍县| 南漳县| 喀喇沁旗| 蒙自县| 黎川县| 青龙| 始兴县| 嵊州市| 华阴市| 怀远县| 天全县| 体育| 福鼎市| 德格县| 肃南| 建湖县| 合江县| 普格县| 新营市| 锡林浩特市| 无棣县| 黄梅县|