我总是在java中工作,理解如果我将任务移植到事件线程,它可以使用它需要的任何合适的锁,并且它永远不会碰到我从事件线程做的任何其他锁。
不幸的是,我的webstart程序有三个事件线程,其中两个是死锁。
我在主线程组中有AWT-EventQueue-0 这挂起在绘制操作中,尝试执行getRowColor()操作以为表格单元格准备渲染器。正如它的绘画一样,它有组件树锁。
我有javawsSecurityThreadGroup的AWT-EventQueue-1,这看起来很无害,看起来它可能在挂起时没有被启用,但它确实挂起了对文本组件的无效()调用(Java控制台?)
最后,我有javawsApplicationThreadGroup的AWT-EventQueue-2。 该特定组件获得写锁定以便设置表数据(其根据行颜色阻止读取)。然后停止对焦点单元格的更新,向下流动scrollRectToVisible(),validateView(),updateCursor(),findComponentAt(),这需要在AWT-EventQueue-0上正在进行的绘制操作所持有的树锁定
读锁/写锁是我们的代码,存在让程序员更少担心线程。我不准备仅仅因为应用程序决定它需要额外的事件线程来并行运行。
最终,我希望我们的应用程序使用单个AWT-EventThread。有没有办法让一个事件线程将请求转移到另一个?或者选择用于绘制的事件线程,或者在invokeLater()/ invokeAndWait()发生时使用?
我不确定这是否重要,但我们确实在我们的申请中使用了FX。
答案 0 :(得分:0)
尝试将写锁定保护的关键部分的范围缩小到仅写入内存。
如果您从关键部分推送后续通知以更新UI,这取决于写入但不是写入的关键部分的一部分,并允许它们通过其他控制路径使用读取锁定,您的死锁问题应该解决。
另一种选择可能是将写入任务分配给后台线程(保持正确的同步;另请参阅java.util.concurrent.ExecutorService),然后在完成写入时发送通知,这些通知可以在显示线程中异步执行。
答案 1 :(得分:0)
我还没有想到这一点,但是我认为我已经足够让任何人追回到最终的解决方案了。我会注意到,显然我的环境在某种程度上是Macintosh计算机独有的,因为我不是第一个在Macintosh系统上看到这个问题的人,而且我以前从未在Windows设置下遇到过这个问题。
Java-Webstart解决问题。它使用标题为javawsApplicationThreadGroup的线程组中的线程调用应用程序main()方法。这将线程放在适当的应用程序上下文中,这会导致任何AWT / Swing调用被传递到此线程组中的AWT-EventQueue-2。 这不仅包括invokeLater(),还包括repaint()调用。
我们也从这个线程启动JavaFX。无论出于何种原因,它将JavaFX应用程序线程放在“系统”线程组中。系统线程组有点棘手,我使用的开发工具(Eclipse IDE / JProfiler)通常不会在这个组中显示线程,这使得它成为任何东西的糟糕选择。我们还将使用InvokeLater()从这里将请求从FX发送到Swing,我确信不缺少repaint()。我没有直接验证这一点,但是快速的经验测试表明来自“系统”下的FX应用程序线程的请求转到了“主”线程组的AWT-EventQueue。
这至少解释了我正在看的内容,并为我提供了解决问题所需的工具。
解决此问题的第一步:在主方法的顶部,找到“主”线程组并启动该组中的新线程以执行实际的应用程序启动。这会导致FX应用程序线程在主线程组以及主线程池下生成。结果,FX和后台任务都将swing请求提交到同一事件队列。这并没有完全解决我的问题(仍然有一些业务逻辑从javawsApplication线程组中的线程运行),但我认为它为我提供了解决方案所需的所有工具。
public static void main(String[] args)
{
startFromMainThreadGroup(args);
}
private static void startFromMainThreadGroup(final String[] args)
{
ThreadGroup tgSystem = Thread.currentThread( ).getThreadGroup( );
ThreadGroup ptg;
while ( (ptg = tgSystem.getParent( )) != null )
{
tgSystem = ptg;
}
ThreadGroup tgMain = null;
ThreadGroup[] groups = new ThreadGroup[tgSystem.activeGroupCount() + 16];
int numGroups = tgSystem.enumerate(groups);
for(int i = 0; i < numGroups; i++)
{
if("main".equals(groups[i].getName()))
{
tgMain = groups[i];
break;
}
}
if(tgMain == null)
{
tgMain = tgSystem; // Fallback
}
Runnable doRun = new Runnable()
{
@Override
public void run()
{
mainImpl(args);
}
};
Thread thread = new Thread(tgMain, doRun, "mainFork");
thread.setDaemon(false); // Explicit
thread.start();
}