当前位置:天才代写 > tutorial > JAVA 教程 > 深入浅出Java多线程(2)-Swing中的EDT(事件分发线程)

深入浅出Java多线程(2)-Swing中的EDT(事件分发线程)

2017-11-10 08:00 星期五 所属: JAVA 教程 浏览:776

副标题#e#

本文主要办理的问题是:

如何使其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多线程(2)-Swing中的EDT(事件分发线程)


#p#副标题#e#

假如在启动一个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.

#p#副标题#e#

加上去今后怎么界面没有任何回响了呢?

代码如下:

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(0, 1));
   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();
     }
   }
}

#p#副标题#e#

打个断点看看怎么了,断点就在这里Thread.sleep (Integer.MAX_VALUE); 看看谁人线程暂停了 看图片:

深入浅出Java多线程(2)-Swing中的EDT(事件分发线程)

#p#分页标题#e#

看到了吧,我们写的谁人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();
     }

#p#副标题#e#

Toolkit.getEventQueue().postEvent(event);把我们写的getFileLock 塞进 了EventQueue.

这下读者对EDT有个认识了吧。

1. EDT 只有一个线程,固然getFileLock是实现Runnable接口,它挪用的时 候不是star要领启动新线程,而是直接挪用run要领。

2. invokeAndWait将你写的getFileLock塞到EventQueue中。

#p#分页标题#e#

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();
     }

#p#副标题#e#

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(0, 1));
   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);

虽然这只是个办理方案,如何友好给给用户提示以及锁定谁人文件就要按照 详细环境而定了。

 

    关键字:

天才代写-代写联系方式