<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    新的起點 新的開始

    快樂生活 !

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

    接深入淺出Java多線程系列(1),本文主要解決的問題是:
    如何使其Swing程序只能運行一個實例?
    拋開Swing, 我們的程序是通過java 命令行啟動一個進程來執行的,該問題也就是說要保證這個進程的唯一性,當然如果能夠訪問系統的接口,得到進程的信息來判斷是否已有進程正在運行,不就解決了嗎?但是如何訪問系統的接口呢?如何要保證在不同的平臺上都是OK的呢?我的思路是用文件鎖,當然我相信肯定有更好的方法,呵呵,希望讀者能夠指出。
    文件鎖是JDK1.4 NIO提出的,可以在讀取一個文件時,獲得文件鎖,這個鎖應該是系統維護的,JVM應該是調用的系統文件鎖機制,例子如下:
    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();
      }
    當代碼獲得鎖后:我們試圖編輯這個文件是就會:


    如果在啟動一個Java Main方法時:
    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();。
    返回的結束是 ture , 也就是得不到文件的鎖。

    這就是對于進程唯一性問題我的解決思路,通過鎖定文件使其再啟動時得不到鎖文件而無法啟動。
    說到這里,跟今天Swing中的EDT好像還沒有關系,對于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);
        啟動Jframe后,Main線程就退出了,上面獲得文件鎖,并持有鎖的邏輯往哪里寫呢? 有人會說事件分發線程EDT,真的嗎?
        由于我沒有做過Swing的項目,僅僅做過個人用的財務管理小軟件,還沒有深入理解過EDT,不管怎么說先把那段邏輯加到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.
        加上去以后怎么界面沒有任何反應了呢?
    代碼如下:
    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();
            }
        }
    }
    打個斷點看看怎么了,斷點就在這里     Thread.sleep(Integer.MAX_VALUE); 看看那個線程暫停了 看圖片:



    看到了吧,我們寫的那個getFileLock 是由AWT-EventQueue-0  線程執行,看右下角調用關系, EventDispathThread 啟動 Run方法, 然后pumpEvents 取事件,然后從EventQueue取到InvocationEvent 執行Dispath
    Dispath調用的就是我們在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加入的那個EventQueue中的呢?當然是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 塞進了EventQueue.
    這下讀者對EDT有個認識了吧。
    1. EDT 只有一個線程, 雖然getFileLock是實現Runnable接口,它調用的時候不是star方法啟動新線程,而是直接調用run方法。
    2.
    invokeAndWait將你寫的getFileLock塞到EventQueue中。
    3.
    Swing 事件機制采用Product Consumer模式 EDT不斷的取EventQueue中的事件執行(消費者)。其他線程可以將事件塞入EventQueue中,比如鼠標點擊Button是,將注冊在BUttion的事件塞入EventQueue中

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

    所以絕對不能將持有鎖的邏輯塞到EventQueue,而應該放到外邊main線程或者其他線程里面。
    提到invokeAndWait,還必須說說invokelater 這兩個區別在哪里呢?
    invokeAndWait與invokelater區別: 看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:當在main方法中調用SwingUtils.invokelater,后,把事件塞入EventQueue就返回了,main線程不會阻塞。
    invokeAndWait: 當在Main方法中調用SwingUtils.invokeAndWait 后,看代碼片段:
            synchronized (lock) {
                Toolkit.getEventQueue().postEvent(event);
                lock.wait();
            }

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

    這下明白了吧!
    總之,對于我們問題最簡單的方法就是是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方法里啟動一個Daemon線程,持有鎖,如果拿不到鎖,就退出 if(fl == null) System.exit(1);
    當然這只是個解決方案,如何友好給給用戶提示以及鎖定那個文件就要根據具體情況而定了。

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

    評論

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

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

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

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

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

    1. 看來你還是沒有理解 invokeAndWait含義,那會阻塞調用該方法的線程,對于例子就是Main.
    2. 呵呵 如果 Integer.MAX_VALUE不夠 可以用Long.MAX_VALUE啊. 當然Socket也可以 但跟你環境有很大關系,況且無緣無故啟動一個Socket 端口,占用資源不說,就殺毒軟件都把你滅了。原理上當然可以!  回復  更多評論   

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

    @advincenting
    用invokeAndWait獲得鎖,在執行后續的程序,有什么問題?  回復  更多評論   


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     

    公告

    Locations of visitors to this pageBlogJava
  • 首頁
  • 新隨筆
  • 聯系
  • 聚合
  • 管理
  • <2008年8月>
    272829303112
    3456789
    10111213141516
    17181920212223
    24252627282930
    31123456

    統計

    常用鏈接

    留言簿(13)

    隨筆分類(71)

    隨筆檔案(179)

    文章檔案(13)

    新聞分類

    IT人的英語學習網站

    JAVA站點

    優秀個人博客鏈接

    官網學習站點

    生活工作站點

    最新隨筆

    搜索

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲精品国产精品国自产观看| 内射少妇36P亚洲区| 日批视频网址免费观看| 婷婷亚洲综合五月天小说| 操美女视频免费网站| 久久国产乱子伦精品免费午夜| 亚洲欧洲日产韩国在线| 国产一级淫片免费播放| 亚欧免费无码aⅴ在线观看| 亚洲欧美综合精品成人导航| 国产亚洲免费的视频看| 大陆一级毛片免费视频观看| 国产午夜无码片免费| 亚洲色无码专区一区| 亚洲色欲久久久综合网| 性一交一乱一视频免费看| 一区二区三区在线观看免费| 亚洲国产福利精品一区二区| 亚洲一区日韩高清中文字幕亚洲 | 亚洲字幕AV一区二区三区四区| 亚洲人成网站18禁止一区| 免费H网站在线观看的| eeuss影院ss奇兵免费com| 亚洲人成网站日本片| 在线亚洲精品自拍| 国产免费黄色大片| 亚州免费一级毛片| 天黑黑影院在线观看视频高清免费 | 亚洲Av永久无码精品黑人 | 亚洲狠狠狠一区二区三区| 亚洲人成国产精品无码| 最近的中文字幕大全免费版| 久久这里只精品热免费99| 老司机精品视频免费| 亚洲欧美日韩中文高清www777| 久久青青草原亚洲AV无码麻豆 | 国产精品免费视频一区| 99久久99久久精品免费看蜜桃| 国产成人无码区免费内射一片色欲 | 亚洲区不卡顿区在线观看| 日韩精品视频免费在线观看|