Java 不同事件线程之间的死锁

Java 不同事件线程之间的死锁,java,multithreading,swing,awt,Java,Multithreading,Swing,Awt,我一直在java中工作,我知道如果我将一个任务分包给事件线程,它可以使用它所需要的任何合适的锁,并且它永远不会与我从事件线程生成的任何其他锁发生冲突 不幸的是,我的webstart程序有三个事件线程,其中两个是死锁 我在主线程组中有AWT-EventQueue-0 这挂起在绘制操作中,试图执行getRowColor()操作来为表单元格准备渲染器。在绘制时,它具有组件树锁 我有javawsSecurityThreadGroup的AWT-EventQueue-1,它似乎是无害的,看起来它在挂起时可能

我一直在java中工作,我知道如果我将一个任务分包给事件线程,它可以使用它所需要的任何合适的锁,并且它永远不会与我从事件线程生成的任何其他锁发生冲突

不幸的是,我的webstart程序有三个事件线程,其中两个是死锁

我在主线程组中有AWT-EventQueue-0 这挂起在绘制操作中,试图执行getRowColor()操作来为表单元格准备渲染器。在绘制时,它具有组件树锁

我有javawsSecurityThreadGroup的AWT-EventQueue-1,它似乎是无害的,看起来它在挂起时可能没有受到影响,但它确实在对文本组件(Java控制台?)的invalidate()调用时挂起

最后,我有JavaWSApplicationReadGroup的AWT-EventQueue-2。 此特定组件获得写锁,用于设置表数据(根据行颜色阻止读取)。然后,这会在聚焦单元格的更新上暂停,该更新向下流动scrollRectToVisible()、validateView()、UpdateCorsor()、findComponentAt(),需要AWT-EventQueue-0上正在进行的绘制操作所持有的树锁

读锁/写锁是我们的代码,它的存在是为了让程序员少担心线程问题。我不准备仅仅因为应用程序决定它需要额外的事件线程来并行运行而取消它

最终,我希望我们的应用程序使用单个AWT事件线程。有没有办法让一个事件线程将请求转移到另一个?或者选择哪个事件线程用于绘制,或者在发生invokeLater()/invokeAndWait()时使用


我不确定这是否重要,但我们确实在应用程序中使用了FX。

尝试将由写锁保护的关键部分的范围缩小为仅写入内存

如果您从关键部分推送后续通知以更新UI(取决于写入但不属于写入的关键部分),并允许它们通过其他控制路径使用读锁,则死锁问题应该得到解决


另一种选择可能是将写入任务转移到后台线程(保持适当的同步;另请参阅),然后在写入完成时发送通知,该通知可以在显示线程中异步执行。

尝试将由写入锁保护的关键部分的范围缩小为仅写入内存

如果您从关键部分推送后续通知以更新UI(取决于写入但不属于写入的关键部分),并允许它们通过其他控制路径使用读锁,则死锁问题应该得到解决


另一种选择可能是将写入任务转移到后台线程(保持适当的同步;另请参见),然后在写入完成时发送通知,这些通知可以在显示线程中异步执行。

我还没有完全解决这个问题,但我想我已经足够让任何人追查到最终的解决方案了。我会注意到,显然我的环境在某种程度上是Macintosh计算机所独有的,因为我不是第一个在Macintosh系统上研究这个问题的人,而且我以前从未在Windows环境下遇到过这个问题

JavaWebStart开启了一切。它使用名为javawsApplicationThreadGroup的线程组中的一个线程调用application main()方法。这会将线程置于适当的应用程序上下文中,这会导致将所有AWT/Swing调用传递到AWT-EventQueue-2,后者位于该线程组中。 这不仅包括invokeLater()调用,还包括repaint()调用

我们也从这个线程启动JavaFX。无论出于何种原因,它都将JavaFX应用程序线程放在“系统”线程组中。系统线程组有点棘手,我使用的开发工具(EclipseIDE/JProfiler)通常不显示该组中的线程,这使得它对于任何事情都是一个糟糕的选择。我们还将使用InvokeLater()从这里将来自FX的请求传递给Swing,并且我确信不会缺少repaint()的。我没有直接验证这一点,但快速的实证测试表明,来自“系统”下的FX应用程序线程的请求进入“主”线程组的AWT事件队列

这至少解释了我在看什么,并为我提供了解决问题所需的工具

解决这个问题的第一步:在main方法的顶部,我找到“main”线程组并在该组中启动一个新线程以执行实际的应用程序启动。这会导致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();
}
publicstaticvoidmain(字符串[]args)
{
从主线程组启动(args);
}
私有静态void startFromMainThreadGroup(最终字符串[]args)
{
ThreadGroup tgSystem=Thread.currentThread().getThreadGroup();
螺纹组;
而((ptg=tgSystem.getParent())!=null)
{
tgSystem=ptg;
}
ThreadGroup tgMain=null;
ThreadGroup[]groups=新的ThreadGroup[tgSystem.activeGroupCount()+16];
int numGroups=tgSystem.enumerate(组);
对于(int i=0;i