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

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

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

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

    快樂生活 !

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

    接深入淺出Java多線程系列(1),本文主要解決的問題是:
    如何使其Swing程序只能運(yùn)行一個(gè)實(shí)例?
    拋開Swing, 我們的程序是通過java 命令行啟動一個(gè)進(jìn)程來執(zhí)行的,該問題也就是說要保證這個(gè)進(jìn)程的唯一性,當(dāng)然如果能夠訪問系統(tǒng)的接口,得到進(jìn)程的信息來判斷是否已有進(jìn)程正在運(yùn)行,不就解決了嗎?但是如何訪問系統(tǒng)的接口呢?如何要保證在不同的平臺上都是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)代碼獲得鎖后:我們試圖編輯這個(gè)文件是就會:


    如果在啟動一個(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 , 也就是得不到文件的鎖。

    這就是對于進(jìn)程唯一性問題我的解決思路,通過鎖定文件使其再啟動時(shí)得不到鎖文件而無法啟動。
    說到這里,跟今天Swing中的EDT好像還沒有關(guān)系,對于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線程就退出了,上面獲得文件鎖,并持有鎖的邏輯往哪里寫呢? 有人會說事件分發(fā)線程EDT,真的嗎?
        由于我沒有做過Swing的項(xiàng)目,僅僅做過個(gè)人用的財(cái)務(wù)管理小軟件,還沒有深入理解過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.
        加上去以后怎么界面沒有任何反應(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 啟動 Run方法, 然后pumpEvents 取事件,然后從EventQueue取到InvocationEvent 執(zhí)行Dispath
    Dispath調(diào)用的就是我們在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.
    這下讀者對EDT有個(gè)認(rèn)識了吧。
    1. EDT 只有一個(gè)線程, 雖然getFileLock是實(shí)現(xiàn)Runnable接口,它調(diào)用的時(shí)候不是star方法啟動新線程,而是直接調(diào)用run方法。
    2.
    invokeAndWait將你寫的getFileLock塞到EventQueue中。
    3.
    Swing 事件機(jī)制采用Product Consumer模式 EDT不斷的取EventQueue中的事件執(zhí)行(消費(fèi)者)。其他線程可以將事件塞入EventQueue中,比如鼠標(biāo)點(diǎn)擊Button是,將注冊在BUttion的事件塞入EventQueue中

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

    所以絕對不能將持有鎖的邏輯塞到EventQueue,而應(yīng)該放到外邊main線程或者其他線程里面。
    提到invokeAndWait,還必須說說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線程不會阻塞。
    invokeAndWait: 當(dāng)在Main方法中調(diào)用SwingUtils.invokeAndWait 后,看代碼片段:
            synchronized (lock) {
                Toolkit.getEventQueue().postEvent(event);
                lock.wait();
            }

    main線程獲得lock 后就wait()了,直到事件分發(fā)線程調(diào)用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方法里啟動一個(gè)Daemon線程,持有鎖,如果拿不到鎖,就退出 if(fl == null) System.exit(1);
    當(dāng)然這只是個(gè)解決方案,如何友好給給用戶提示以及鎖定那個(gè)文件就要根據(jù)具體情況而定了。

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

    評論

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

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

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

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

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

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

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

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


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


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

    公告

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

    統(tǒng)計(jì)

    常用鏈接

    留言簿(13)

    隨筆分類(71)

    隨筆檔案(179)

    文章檔案(13)

    新聞分類

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

    JAVA站點(diǎn)

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

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

    生活工作站點(diǎn)

    最新隨筆

    搜索

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 国产日韩亚洲大尺度高清| 免费看又黄又爽又猛的视频软件| 日韩人妻无码免费视频一区二区三区| 亚洲AV日韩AV永久无码色欲| 久久综合亚洲色hezyo| 精品一区二区三区高清免费观看| 精品无码专区亚洲| 在线观看免费无码视频| 在线观看特色大片免费视频 | 亚洲精品天堂在线观看| 一级特黄特色的免费大片视频| 亚洲爆乳大丰满无码专区| 视频免费在线观看| 成人国产mv免费视频| 亚洲av日韩av激情亚洲| 欧美亚洲精品一区二区| 99热免费在线观看| 免费人成年轻人电影| 亚洲喷奶水中文字幕电影| 久久成人永久免费播放| 成人免费的性色视频| 久久久亚洲精品蜜桃臀| 亚洲人成77777在线播放网站不卡| 色偷偷亚洲女人天堂观看欧| 无码的免费不卡毛片视频| 久草视频免费在线观看| 亚洲乱码中文字幕综合| 噜噜综合亚洲AV中文无码| 国产成人精品久久免费动漫| 亚洲中文字幕无码久久2017| 亚洲乱码国产乱码精华| 57pao国产成永久免费视频| 亚洲日本乱码在线观看| 免费人成视频在线观看免费| 成人爽A毛片免费看| 亚洲视频网站在线观看| 九九精品成人免费国产片| 亚洲精品无码日韩国产不卡?V| 婷婷亚洲综合五月天小说| 国产三级在线免费观看| 国产亚洲福利一区二区免费看|