Java Swing EDT:如何通过SwingUtilities.invokeAndWait知道哪些线程正在等待EventDisplay的执行?
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java Swing EDT:如何通过SwingUtilities.invokeAndWait知道哪些线程正在等待EventDisplay的执行?,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8305字,纯文字阅读大概需要12分钟。
内容图文
我有一个非常复杂的问题.在当前的项目中,我有一个用Java编写的GUI和一个用C编写的计算引擎.
这些是Java中的显示,可以访问C中的数据,并且我在并发方面遇到一些问题.
这段代码有很长的一段话,所以我不能只重写所有内容(即使偶尔需要它:p).
引擎修改数据时,将获取互斥量.从这边来看很干净.
问题是GUI.它是Java Swing,它可以不受任何控制地从EventDispatchThread或任何线程访问数据,并通过对内核的每次统一访问来获取c互斥锁(通过JNI)(这对于性能和数据一致性而言是不好的). .
我已经对其进行了重构,以将锁定代码封装在Java中的“ NativeMutex”中,该代码从JNI调用本机函数锁定和解锁.
我想编写一个“ ReentrantNativeLock”,以避免全部重写,而只添加一些高级锁.
但是此ReentrantNativeLock必须处理EventDisplayThread.
我已定义此锁实现必须避免EDT使用互斥锁(通过从EDT调用lock方法时引发异常),而仅在锁已由另一个线程拥有时返回(以处理SwingUtilities.InvokeAndWait而不返回).重写此应用程序的所有脏代码)
从概念上讲,这没关系,因为我专注于C引擎和JAVA GUI之间的同步,但是从Java方面来看,这并不安全.
所以我想走得更远.如果我可以知道哪些线程正在等待EDT(那些被称为“ InvokeAndWait”的线程),则可以实现更安全的方法.
我将能够检查所有者线程是否正在等待EDT,并避免一些难以理解但可能会导致我自己和同事烦恼的错误.
因此,我如何知道哪些线程正在等待EDT(哪些线程被称为“ InvokeAndWait”)
(如果我描述了上下文,那是因为我乐于听取其他可以解决我的问题的想法……只要它们并不意味着很多重写.)
正如一些评论使我相信对上下文的描述不够好,我发布了一些代码,希望这些代码可以明确我的问题.
它是一个基本的Decorator,m_NativeLock是不可重入的nativeLock.
public class ReentrantNativeLock implements NativeLock {
/**
* Logger
*/
private static final Logger LOGGER = Logger.getLogger(ReentrantNativeLock.class);
public ReentrantNativeLock(NativeLock adaptee) {
m_NativeLock = adaptee;
}
public void lock() {
if (!SwingUtilities.isEventDispatchThread()) {
m_ReentrantLock.lock();
if (m_ReentrantLock.getHoldCount() == 1) { // Only the first lock from a thread lock the engine, to avoid deadlock with the same thread
m_NativeLock.lock();
}
}
else if (m_ReentrantLock.isLocked()) {
// It's EDT, but some thread has lock the mutex, so it's ok... We assume that the locked thread as called SwingUtilities.invokeAndWait... But if I can check it, it will be better.
LOGGER.debug("Lock depuis EDT (neutre). Le lock a été verrouillé, l'accès moteur est (à priori) safe", new Exception());
}
else {
// We try to avoid this case, so we throw an exception which will be tracked and avoided before release, if possible
throw new UnsupportedOperationException("L'EDT ne doit pas locker elle-même le moteur.");
}
}
public boolean tryLock() {
if (!SwingUtilities.isEventDispatchThread()) {
boolean result = m_ReentrantLock.tryLock();
if (result && m_ReentrantLock.getHoldCount() == 1) {
result = m_NativeLock.tryLock();// Only the first lock from a thread lock the engine, to avoid deadlock with the same thread
if (!result) {
m_ReentrantLock.unlock(); // If the trylock on engin fail, we free the lock (I will put it in a try{}finally{} if I valid this solution.
}
}
return result;
}
else if (m_ReentrantLock.isLocked()) {
// It's EDT, but some thread has lock the mutex, so it's ok... We assume that the locked thread as called SwingUtilities.invokeAndWait... But if I can check it, it will be better.
LOGGER.debug("Lock depuis EDT (neutre). Le lock a été verrouillé, l'accès moteur est (à priori) safe", new Exception());
return true;
}
else {
// We try to avoid this case, so we throw an exception which will be tracked and avoided before release, if possible
throw new UnsupportedOperationException("L'EDT ne doit pas locker elle-même le moteur.");
}
}
public void unlock() {
if (!SwingUtilities.isEventDispatchThread()) {
if (m_ReentrantLock.getHoldCount() == 1) {
m_NativeLock.unlock();
}
m_ReentrantLock.unlock();
}
else {
LOGGER.debug("Unlock depuis EDT (neutre). Le lock a été verrouillé, l'accès moteur est (à priori) safe", new Exception());
}
}
final ReentrantLock m_ReentrantLock = new ReentrantLock();
final NativeLock m_NativeLock;
}
解决方法:
您可以做的是拥有自己的EventQueue,它记录要分派的事件,从中创建事件的线程以及线程是否正在等待分派事件(因此,在线程调用invokeAndWait的情况下).
首先,推送您自己的队列:
ThreadTrackingEventQueue queue = new ThreadTrackingEventQueue();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue);
在您执行队列时:
>覆盖postEvent,检查它是否是InvocationEvent,是否正在等待通知.在这种情况下,请跟踪线程和相应的事件
>覆盖dispatchEvent以将调用线程取消标记为等待EDT.
完整的示例(注意,它在EDT上休眠以发生冲突,但是绝对不能在应用程序中完成):
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InvocationEvent;
import java.lang.reflect.Field;
import java.util.Hashtable;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestEventQueue {
private final ThreadTrackingEventQueue queue;
public static class ThreadTrackingEventQueue extends EventQueue {
private Field notifierField;
private Hashtable<AWTEvent, Thread> waitingThreads = new Hashtable<AWTEvent, Thread>();
public ThreadTrackingEventQueue() throws NoSuchFieldException, SecurityException {
notifierField = InvocationEvent.class.getDeclaredField("notifier");
notifierField.setAccessible(true);
}
@Override
public void postEvent(AWTEvent event) {
if (!SwingUtilities.isEventDispatchThread() && event.getClass() == InvocationEvent.class) {
try {
Object object = notifierField.get(event);
if (object != null) {
// This thread is waiting to be notified: record it
waitingThreads.put(event, Thread.currentThread());
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
super.postEvent(event);
}
@Override
protected void dispatchEvent(AWTEvent event) {
try {
super.dispatchEvent(event);
} finally {
if (event.getClass() == InvocationEvent.class) {
waitingThreads.remove(event);
}
}
}
public Hashtable<AWTEvent, Thread> getWaitingThreads() {
return waitingThreads;
}
}
public TestEventQueue(ThreadTrackingEventQueue queue) {
this.queue = queue;
}
private void initUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextArea textArea = new JTextArea(30, 80);
JButton button = new JButton("Start");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
start();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
frame.add(new JScrollPane(textArea));
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
Timer t = new Timer(100, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Hashtable<AWTEvent, Thread> waitingThreads = (Hashtable<AWTEvent, Thread>) queue.getWaitingThreads().clone();
if (waitingThreads.size() > 0) {
for (Thread t : queue.getWaitingThreads().values()) {
textArea.append("Thread " + t.getName() + " is waiting for EDT\n");
}
} else {
textArea.append("No threads are waiting\n");
}
}
});
t.start();
}
protected void start() throws InterruptedException {
final Random random = new Random();
ExecutorService pool = Executors.newFixedThreadPool(50);
for (int i = 0; i < 50; i++) {
pool.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
System.out.println("sleeping before invoke and wait");
Thread.sleep(random.nextInt(2000) + 200);
System.out.println("invoke and wait");
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
try {
System.out.println("sleeping on EDT, bwark :-(");
// Very very bad, but trying to make collisions
// happen
Thread.sleep(random.nextInt(200) + 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
return true;
}
});
}
System.out.println("Invoked all");
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
final ThreadTrackingEventQueue queue = new ThreadTrackingEventQueue();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue);
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
TestEventQueue test = new TestEventQueue(queue);
test.initUI();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
内容总结
以上是互联网集市为您收集整理的Java Swing EDT:如何通过SwingUtilities.invokeAndWait知道哪些线程正在等待EventDisplay的执行?全部内容,希望文章能够帮你解决Java Swing EDT:如何通过SwingUtilities.invokeAndWait知道哪些线程正在等待EventDisplay的执行?所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。