日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

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

發(fā)布時間:2023/12/10 java 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 事件分发线程_深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) [转载]... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本系列文章導(dǎo)航

本文主要解決的問題是:

如何使其Swing程序只能運(yùn)行一個實(shí)例?

拋開Swing, 我們的程序是通過java 命令行啟動一個進(jìn)程來執(zhí)行的,該問題也就是說要保證這個進(jìn)程的唯一性,當(dāng)然如果能夠訪問系統(tǒng)的接口,得到進(jìn)程的信息來判斷是否已有進(jìn)程正在運(yùn)行,不就解決 了嗎?但是如何訪問系統(tǒng)的接口呢?如何要保證在不同的平臺上都是OK的呢?我的思路是用文件鎖,當(dāng)然我相信肯定有更好的方法,呵呵,希望讀者能夠指出。

文件鎖是JDK1.4 NIO提出的,可以在讀取一個文件時,獲得文件鎖,這個鎖應(yīng)該是系統(tǒng)維護(hù)的,JVM應(yīng)該是調(diào)用的系統(tǒng)文件鎖機(jī)制,例子如下:

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

}

}

當(dāng)代碼獲得鎖后:我們試圖編輯這個文件是就會:

如果在啟動一個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();

}

}

返回的結(jié)果是 ture , 也就是得不到文件的鎖。

這就是對于進(jìn)程唯一性問題我的解決思路,通過鎖定文件使其再啟動時得不到鎖文件而無法啟動。

說到這里,跟今天Swing中的EDT好像還沒有關(guān)系,對于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線程就退出了,上面獲得文件鎖,并持有鎖的邏輯往哪里寫呢? 有人會說事件分發(fā)線程EDT,真的嗎?

由于我沒有做過Swing的項(xiàng)目,僅僅做過個人用的財務(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)了呢?

代碼如下:

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

}

}

}

打個斷點(diǎn)看看怎么了,斷點(diǎn)就在這里   Thread.sleep(Integer.MAX_VALUE); 看看那個線程暫停了 看圖片:

看到了吧,我們寫的那個getFileLock 是由AWT-EventQueue-0 線程執(zhí)行,看右下角調(diào)用關(guān)系,

EventDispathThread 啟動 Run方法, 然后pumpEvents

取事件,然后從EventQueue取到InvocationEvent 執(zhí)行Dispath

Dispath調(diào)用的就是我們在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中的呢?當(dāng)然是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 塞進(jìn)了EventQueue.

這下讀者對EDT有個認(rèn)識了吧。

1. EDT 只有一個線程, 雖然getFileLock是實(shí)現(xiàn)Runnable接口,它調(diào)用的時候不是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 這兩個區(qū)別在哪里呢?

invokeAndWait與invokelater區(qū)別: 看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:當(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線程里,或者在其他線程里處理。

最后的解決方案是:

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

當(dāng)然這只是個解決方案,如何友好給給用戶提示以及鎖定那個文件就要根據(jù)具體情況而定了。

總結(jié)

以上是生活随笔為你收集整理的java 事件分发线程_深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) [转载]...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。