本系列文章导航

深入浅出Java多线程(1)-方法 join

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

深入浅出多线程(3)-Future异步模式以及在JDK1.5Concurrent包中的实现

深入浅出多线程(4)对CachedThreadPool OutOfMemoryError难题的一些想法

深入浅出多线程(5)以并行包线程池为例说说线程池的设计需求及使用

深入浅出多线程(6)分析并行包线程池的设计与实现

本文主要解决的问题是:

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

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

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

package concurrentstudy;

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方法时:

package concurrentstudy;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock; 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 concurrentstudy;

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


  打个断点看看怎么了,断点就在这里   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 concurrentstudy;

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

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

博文来源:http://www.cqzol.com/programming/Java/200812/198649.html

深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) [转载]的更多相关文章

  1. 使用泛型SwingWorker与EDT事件分发线程保持通讯

    为什么要使用SwingWorker 在swing开发中,如果一个应用程序,执行一些任务,需要大量的时间来完成,比如下载一个大文件或执行一个复杂的数据库查询. 我们假设这些任务是由用户使用一个按钮触发的 ...

  2. “全栈2019”Java多线程第十五章:当后台线程遇到finally

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. “全栈2019”Java多线程第四章:设置和获取线程名称

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  4. 深入浅出Java多线程

    Java给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特别的形式,但多线程使用了 ...

  5. Java多线程系列--“基础篇”09之 interrupt()和线程终止方式

    概要 本章,会对线程的interrupt()中断和终止方式进行介绍.涉及到的内容包括:1. interrupt()说明2. 终止线程的方式2.1 终止处于“阻塞状态”的线程2.2 终止处于“运行状态” ...

  6. Java多线程初学者指南(7):向线程传递数据的三种方法

    在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果.但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别.由于线程 ...

  7. java多线程(三)-Executors实现的几种线程池以及Callable

    从java5开始,类库中引入了很多新的管理调度线程的API,最常用的就是Executor(执行器)框架.Executor帮助程序员管理Thread对象,简化了并发编程,它其实就是在 提供了一个中间层, ...

  8. java 多线程总结篇3之——生命周期和线程同步

    一.生命周期 线程的生命周期全在一张图中,理解此图是基本: 线程状态图 一.新建和就绪状态 当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时它和其他的Java对象一样,仅仅由Jav ...

  9. java多线程系类:基础篇:07线程休眠

    概要 本章,会对Thread中sleep()方法进行介绍.涉及到的内容包括:1. sleep()介绍2. sleep()示例3. sleep() 与 wait()的比较 转载请注明出处:http:// ...

随机推荐

  1. C语言和C++中的字符串(string)

    知识内容: 1.C\C++字符串简述 2.C字符串相关操作 3.C++ string类相关操作 一.C\C++字符串简述 1.C语言字符串 C语言字符串是字符的数组.单字节字符串顺序存放各个字符串,并 ...

  2. Android屏幕适配方案——基于最小宽度(Smallest-width)限定符

    转自:https://www.cnblogs.com/error404/p/3815739.html 一.关于布局适配建议 1.不要使用绝对布局 2.尽量使用match_parent 而不是fill_ ...

  3. 深入浅出 Java Concurrency (8): 加锁的原理 (Lock.lock)

    接上篇,这篇从Lock.lock/unlock开始.特别说明在没有特殊情况下所有程序.API.文档都是基于JDK 6.0的. public void java.util.concurrent.lock ...

  4. Django中多种重定向方法使用

    本文主要讲解使用HttpResponseRedirect.redirect.reverse以及配置文件中配置URL等重定向方法 本文使用了Django1.8.2 使用场景,例如在表单一中提交数据后,需 ...

  5. multiprocessing.dummy

    昨晚发现放在腾讯云主机上通过crontab定时执行用以爬去斗鱼分类页面数据的爬虫在执行的时候速度特别慢,于是想通过多线程来提高效率. 打开浏览器,键入关键字"python 多线程" ...

  6. JNI 里使用STL

    JNI里的c或者c++ 调用stl 的时候,比如引入map头文件: #include <map> 在cygwin使用NDK编译的时候,会提示: fatal error:map: No su ...

  7. Ajax 简单实例,其实就是js里面内容有些不同而已(转载)

    这些时间,瞎子也看得见,AJAX正大踏步的朝我们走来.不管我们是拥护也好,反对也罢,还是视而不见,AJAX像一阵潮流,席转了我们所有的人. 关于AJAX的定义也好,大话也好,早有人在网上发表了汗牛充栋 ...

  8. oracel 查询删除重复记录的几种方法

    建表语句CREATE TABLE Persons(PersonID int,           LastName varchar(255),FirstName varchar(255),Addres ...

  9. python's descriptor

    [python's descriptor] 1.实现了以下三个方法任意一个的,且作为成员变量存在的对象,就是descriptor. 1)object.__get__(self, instance, o ...

  10. 【LA2957 训练指南】运送超级计算机【二分,最大流】

    题意: 宇宙中有n个星球,你的任务是用最短的时间把k个超级计算机从星球S运送到星球T.每个超级计算机需要一整艘飞船来运输.行星之间有m条双向隧道,每条隧道需要一整天的时间来通过,且不能有两艘飞船同时使 ...