本系列文章导航

本文主要解决的问题是:

如何使其Swing程序只能运行一个实例?

抛开Swing, 我们的程序是通过java 命令行启动一个进程来执行的,该问题也就是说要保证这个进程的唯一性,当然如果能够访问系统的接口,得到进程的信息来判断是否已有进程正在运行,不就解决 了吗?但是如何访问系统的接口呢?如何要保证在不同的平台上都是OK的呢?我的思路是用文件锁,当然我相信肯定有更好的方法,呵呵,希望读者能够指出。

文件锁是JDK1.4 NIO提出的,可以在读取一个文件时,获得文件锁,这个锁应该是系统维护的,JVM应该是调用的系统文件锁机制,例子如下:

packageconcurrentstudy;importjava.io.FileNotFoundException;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.nio.channels.FileChannel;importjava.nio.channels.FileLock;/***@authorvma*/

public classtemp1 {public static void main(String args[]) throwsFileNotFoundException, 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方法时:

packageconcurrentstudy;importjava.io.FileNotFoundException;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.nio.channels.FileChannel;importjava.nio.channels.FileLock;public classTemp2 {public static void main(String args[]) throwsFileNotFoundException, 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 voidmain(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= newSwingApplication();

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.

加上去以后怎么界面没有任何反应了呢?

代码如下:

packageconcurrentstudy;importjava.awt.BorderLayout;importjava.awt.Component;importjava.awt.GridLayout;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjava.awt.event.KeyEvent;importjava.io.FileNotFoundException;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.lang.reflect.InvocationTargetException;importjava.nio.channels.FileChannel;importjava.nio.channels.FileLock;importjava.util.logging.Level;importjava.util.logging.Logger;importjavax.swing.BorderFactory;importjavax.swing.JButton;importjavax.swing.JFrame;importjavax.swing.JLabel;importjavax.swing.JPanel;importjavax.swing.SwingUtilities;importjavax.swing.UIManager;public classSwingApplication {private static String labelPrefix = "Number of button clicks: ";private int numClicks = 0;publicComponent createComponents() {final JLabel label = new JLabel(labelPrefix + "0");

JButton button= new JButton("I'm a Swing button!");

button.setMnemonic(KeyEvent.VK_I);

button.addActionListener(newActionListener() {public voidactionPerformed(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= newJPanel();

pane.setBorder(BorderFactory.createEmptyBorder(30, //top

30, //left

10, //bottom

30) //right

);

pane.setLayout(new GridLayout(0, 1));

pane.add(button);

pane.add(label);returnpane;

}public static void main(String[] args) throwsInterruptedException {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= newSwingApplication();

Component contents=app.createComponents();

frame.getContentPane().add(contents, BorderLayout.CENTER);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.pack();

frame.setVisible(true);try{

SwingUtilities.invokeAndWait(newgetFileLock());

}catch(InvocationTargetException ex) {

ex.printStackTrace();

}

}

}class getFileLock implementsRunnable {public voidrun() {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 voiddispatch() {if(catchExceptions) {try{

runnable.run();

}catch(Throwable t) {if (t instanceofException) {

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) throwsInterruptedException, InvocationTargetException {if(EventQueue.isDispatchThread()) {throw new Error("Cannot call invokeAndWait from the event dispatcher thread");

}classAWTInvocationLock {}

Object lock= newAWTInvocationLock();

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 voidinvokeLater(Runnable runnable) {

Toolkit.getEventQueue().postEvent(newInvocationEvent(Toolkit.getDefaultToolkit(), runnable));

}public static void invokeAndWait(Runnable runnable) throwsInterruptedException, InvocationTargetException {if(EventQueue.isDispatchThread()) {throw new Error("Cannot call invokeAndWait from the event dispatcher thread");

}classAWTInvocationLock {}

Object lock= newAWTInvocationLock();

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 newInvocationTargetException(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线程里,或者在其他线程里处理。

最后的解决方案是:

packageconcurrentstudy;importjava.awt.BorderLayout;importjava.awt.Component;importjava.awt.GridLayout;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjava.awt.event.KeyEvent;importjava.io.FileNotFoundException;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.nio.channels.FileChannel;importjava.nio.channels.FileLock;importjavax.swing.BorderFactory;importjavax.swing.JButton;importjavax.swing.JFrame;importjavax.swing.JLabel;importjavax.swing.JPanel;importjavax.swing.UIManager;public classSwingApplication {private static String labelPrefix = "Number of button clicks: ";private int numClicks = 0;publicComponent createComponents() {final JLabel label = new JLabel(labelPrefix + "0");

JButton button= new JButton("I'm a Swing button!");

button.setMnemonic(KeyEvent.VK_I);

button.addActionListener(newActionListener() {public voidactionPerformed(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= newJPanel();

pane.setBorder(BorderFactory.createEmptyBorder(30, //top

30, //left

10, //bottom

30) //right

);

pane.setLayout(new GridLayout(0, 1));

pane.add(button);

pane.add(label);returnpane;

}public static void main(String[] args) throwsInterruptedException {try{

UIManager.setLookAndFeel(UIManager

.getCrossPlatformLookAndFeelClassName());

}catch(Exception e) {

}

Thread t= new Thread(newgetFileLock());

t.setDaemon(true);

t.start();//Create the top-level container and add contents to it.

JFrame frame = new JFrame("SwingApplication");

SwingApplication app= newSwingApplication();

Component contents=app.createComponents();

frame.getContentPane().add(contents, BorderLayout.CENTER);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.pack();

frame.setVisible(true);

}

}class getFileLock implementsRunnable {public voidrun() {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);

当然这只是个解决方案,如何友好给给用户提示以及锁定那个文件就要根据具体情况而定了。

java 事件分发线程_深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) [转载]...相关推荐

  1. java项目----教务管理系统_基于Java的教务管理系统

    java项目----教务管理系统_基于Java的教务管理系统 2022-04-22 18:18·java基础 最近为客户开发了一套学校用教务管理系统,主要实现学生.课程.老师.选课等相关的信息化管理功 ...

  2. Swing中的并发-使用SwingWorker线程模式

    Swing中的并发-使用SwingWorker线程模式 原创作品http://zhangjunhd.blog.51cto.com/113473/34727 Blog: [url]http://zhan ...

  3. java线程池1001java线程池_深入浅出Java(Android )线程池ThreadPoolExecutor

    前言 关于线程池 在Java/Android开发中,设计到并发的请求,那基本上是离不开线程池了.用线程池的好处: 1.减少线程频繁创建.销毁的开销: 2.好控制并发量,降低OOM的可能,至于原因文中会 ...

  4. java多线程为啥一直用的一个线程_一个Java多线程的问题,颠覆了我多年的认知!...

    作者 | ithuangqing 来源 | 编码之外(ID:ithuangqing) 碰见个奇怪的多线程问题 小白们也不用怕,今天的文章你们都能看得懂,最近的学习中,碰到这样的一个问题: Java创建 ...

  5. java 线程_理解java多线程

    树枝头玩魔法班卓琴弦乐器的生物与发光的蝴蝶,数字艺术风格,插画绘画creature with java多线程 1.实现Runnable接口相比于继承Thread类: (1)适合多个相同的程序代码的线程 ...

  6. Java线程池状态判断源码_深入浅出Java线程池:源码篇

    前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...

  7. java 1000个线程_关于Java多线程的一个问题

    声明要同步的方法时,例如: public synchronized void foo() { // Do something } 编译器将它看作是你写的: public void foo() { sy ...

  8. java 字节码增强原理_深入浅出Java探针技术1--基于java agent的字节码增强案例

    Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...

  9. java探针 字节码增强_深入浅出Java探针技术1--基于java agent的字节码增强案例

    Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...

最新文章

  1. Python函数封装:利用正则表达式compile、findall对多组关键词进行模糊查询并返回统计个数,findall截取两个指定符号之间的内容
  2. Oracle 11gR2 安装 example(范例数据库)
  3. boost::range_const_reverse_iterator相关的测试程序
  4. web.xml 文件中的配置节的加载顺序
  5. Java EE与Java SE:Oracle是否放弃了企业软件?
  6. Python一句话过滤字符串中的空白字符和中英文标点
  7. unity velocity_Unity动画系统详解5:BlendTree混合树是什么?
  8. JavaScript开发规范要求
  9. 【Java练习题】Java 程序的输出 | 第十一套(含解析)
  10. nsis升级包_NSIS制作软件升级安装包完整教程
  11. 思科模拟器(交换机,路由器综合项目)
  12. JMP系列-基础操作(一)
  13. 基于轨迹的游客行为特征分析
  14. 嵌入式开发培训好学吗?嵌入式培训课程怎么选?
  15. 理财通app的设计与实现(三)
  16. 自动驾驶分级 - 练习测验
  17. QTreeWidget去掉虚线框
  18. NAV导航网格寻路(4) -- 生成nav网格
  19. 科技云报道:从Robot到Cobot,人机共融正在开创一个时代
  20. 新闻联播变脸报道“嫦娥发射”才更酷

热门文章

  1. oracle 中表,oracle中表操作
  2. Linux 系统应用编程——网络编程(I/O模型)
  3. 【Mac】sublime text3标题栏显示文件完整路径
  4. [react] 在React怎么使用Context?
  5. React开发(165):ant design validateFields
  6. 前端学习(3317):connect 2
  7. Taro+react开发(24)--this.state和this.props
  8. [vue] webpack打包vue速度太慢怎么办?
  9. [vue] 说下你的vue项目的目录结构,如果是大型项目你该怎么划分结构和划分组件呢?
  10. [css] 如何实现换肤功能?