我有一个没有反应的应用程序,似乎处于僵局或僵局之中.看下面的两个线程.请注意,My-Thread @ 101c线程阻止AWT-EventQueue-0 @ 301.但是,My-Thread刚刚调用了 java.awt.EventQueue.invokeAndWait().所以AWT-EventQueue-0阻止我的线程(我相信).
My-Thread@101c,priority=5,in group 'main',status: 'WAIT'
     blocks AWT-EventQueue-0@301
      at java.lang.Object.wait(Object.java:-1)
      at java.lang.Object.wait(Object.java:485)
      at java.awt.EventQueue.invokeAndWait(UnkNown Source:-1)
      at javax.swing.SwingUtilities.invokeAndWait(UnkNown Source:-1)
      at com.acme.ui.viewbuilder.renderOnEDT(viewbuilder.java:157)
        .
        .
        .
      at com.acme.util.Job.run(Job.java:425)
      at java.lang.Thread.run(UnkNown Source:-1)

AWT-EventQueue-0@301,priority=6,status: 'MONITOR'
     waiting for My-Thread@101c
      at com.acme.persistence.TransactionalSystemImpl.executeImpl(TransactionalSystemImpl.java:134)
        .
        .
        .
      at com.acme.ui.components.MyTextAreaComponent$MyDocumentListener.insertUpdate(MyTextAreaComponent.java:916)
      at javax.swing.text.AbstractDocument.fireInsertUpdate(UnkNown Source:-1)
      at javax.swing.text.AbstractDocument.handleInsertString(UnkNown Source:-1)
      at javax.swing.text.AbstractDocument$DefaultFilterBypass.replace(UnkNown Source:-1)
      at javax.swing.text.DocumentFilter.replace(UnkNown Source:-1)
      at com.acme.ui.components.FilteredDocument$InputDocumentFilter.replace(FilteredDocument.java:204)
      at javax.swing.text.AbstractDocument.replace(UnkNown Source:-1)
      at javax.swing.text.JTextComponent.replaceSelection(UnkNown Source:-1)
      at javax.swing.text.DefaultEditorKit$DefaultKeyTypedAction.actionPerformed(UnkNown Source:-1)
      at javax.swing.SwingUtilities.notifyAction(UnkNown Source:-1)
      at javax.swing.JComponent.processKeyBinding(UnkNown Source:-1)
      at javax.swing.JComponent.processKeyBindings(UnkNown Source:-1)
      at javax.swing.JComponent.processKeyEvent(UnkNown Source:-1)
      at java.awt.Component.processEvent(UnkNown Source:-1)
      at java.awt.Container.processEvent(UnkNown Source:-1)
      at java.awt.Component.dispatchEventImpl(UnkNown Source:-1)
      at java.awt.Container.dispatchEventImpl(UnkNown Source:-1)
      at java.awt.Component.dispatchEvent(UnkNown Source:-1)
      at java.awt.KeyboardFocusManager.redispatchEvent(UnkNown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(UnkNown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.predispatchKeyEvent(UnkNown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(UnkNown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.dispatchEvent(UnkNown Source:-1)
      at java.awt.Component.dispatchEventImpl(UnkNown Source:-1)
      at java.awt.Container.dispatchEventImpl(UnkNown Source:-1)
      at java.awt.Window.dispatchEventImpl(UnkNown Source:-1)
      at java.awt.Component.dispatchEvent(UnkNown Source:-1)
      at java.awt.EventQueue.dispatchEvent(UnkNown Source:-1)
      at java.awt.EventdispatchThread.pumpOneEventForFilters(UnkNown Source:-1)
      at java.awt.EventdispatchThread.pumpEventsForFilter(UnkNown Source:-1)
      at java.awt.EventdispatchThread.pumpEventsForHierarchy(UnkNown Source:-1)
      at java.awt.EventdispatchThread.pumpEvents(UnkNown Source:-1)
      at java.awt.EventdispatchThread.pumpEvents(UnkNown Source:-1)
      at java.awt.EventdispatchThread.run(UnkNown Source:-1)

这是TransactionalSystemImpl.executeImpl方法:

private synchronized Object executeImpl(Transaction xact,boolean commit) {
    final Object result;

    try {
        if (commit) { // this is line 134
            clock.latch();
            synchronized(pendingEntries) {
                if (xactLatchCount > 0) {
                    pendingEntries.add(xact);
                } else {
                    xactLog.write(new TransactionEntry(xact,clock.time()));
                }
            }
        }

        final TransactionExecutor executor = transactionExecutorFactory.create(
                xact.getClass().getSimpleName()
        );

        if (executor == null) {
            throw new IllegalStateException("Failed to create transaction executor for transaction: " + xact.getClass().getName());
        }

        result = executor.execute(xact);

    } finally {
        if (commit) clock.unlatch();
    }

    return result;
}

有谁知道这里发生了什么或如何解决?

解决方法

在我熟悉的Swing开发人员中,invokeAndWait似乎是有问题的,但也许这并不像我以前所知道的那样.我似乎回想起在关于正确使用invokeAndWait的困难的文档中看到严厉的警告,但是我很难找到任何东西.在当前的官方文档中找不到任何东西.我唯一能够找到的是 Swing Tutorial from 2005的旧版本:(网络档案)

If you use invokeAndWait,make sure that the thread that calls invokeAndWait does not hold any locks that other threads might need while the call is occurring.

不幸的是,这条线似乎从目前的Swing教程中消失了.即使这是一个轻描淡写;我喜欢这样说:“如果你使用invokeAndWait,调用invokeAndWait的线程在调用发生时不能保持其他线程可能需要的任何锁.”一般来说,很难知道在任何给定时间内其他线程可能需要什么锁,最安全的策略可能是确保线程调用invokeAndWait根本不会保存任何锁.

(这很难做,这就是为什么我上面说的invokeAndWait是有问题的,我也知道JavaFX的设计者 – 基本上是一个Swing替换 – 在javafx.application.Platform类中定义了一个方法,称为runLater,在功能上等同于invokelater但是它们故意省略了一个等效的方法来调用和调用,因为它很难正确使用.)

原因是从第一个原则得出的相当直接.考虑一个类似于OP描述的系统,有两个线程:MyThread和事件调度线程(EDT). MyThread对对象L进行锁定,然后调用invokeAndWait.这个帖子事件E1,并等待它由EDT处理.假设E1的处理程序需要锁定L.当EDT处理事件E1时,它尝试将锁定在L上.此锁由MyThread保存,在EDT处理E1之前不会放弃该锁,但该处理被阻止由MyThread.所以我们有僵局.

以下是这种情况的变化.假设我们确保处理E1不需要锁定L.这将是安全的吗?否.如果在MyThread调用invokeAndWait之前,将事件E0发布到事件队列,并且E0的处理程序需要在L上锁定,那么问题仍然可能发生.如前所述,MyThread将锁定在L上,因此E0的处理被阻止. E1在事件队列中处于E0之后,E1的处理也被阻塞.由于MyThread正在等待E1被处理,而且被E0阻塞,这又被阻塞等待MyThread放弃L上的锁定,我们再次出现死锁.

这听起来与OP应用程序中发生的情况非常相似.根据OP对this answer的评论,

Yes,renderOnEDT is synchronized on something way up in the call stack,the com.acme.persistence.TransactionalSystemImpl.executeImpl method which is synchronized. And renderOnEDT is waiting to enter that same method. So,that is the source of the deadlock it looks like. Now I have to figure out how to fix it.

我们没有一个完整的图片,但这可能足以继续. renderOnEDT正在从MyThread中调用,当它在invokeAndWait中被阻塞时,该对象正在对某个东西进行锁定.正在等待EDT的事件处理,但是我们可以看到EDT被MyThread所持有的东西阻挡.我们不能完全明确地说明哪个对象,但这并不重要 – EDT被MyThread所锁定的明确阻止,而MyThread显然正在等待EDT来处理事件:因此,死锁.

还要注意,我们可以肯定的是,EDT目前不处理由invokeAndWait发布的事件(类似于上面我的场景中的E1).如果是这样,每次都会发生僵局.它似乎只发生在有时候,根据07年3月的OP的评论,当用户快速打字时.所以我敢打赌,EDT正在处理的事件是一个按键,恰好在MyThread锁定之后被发布到事件队列,但在MyThread调用invokeAndWait之前将E1发布到事件队列中,因此它类似于E0在我上面的场景中

到目前为止,这可能大概是从其他答案和OP对这些答案的意见拼合在一起的问题的回顾.在我们继续讨论一个解决方案之前,下面是我对OP应用程序的一些假设:

>它是多线程的,所以各种对象必须同步才能正常工作.这包括来自Swing事件处理程序的调用,这可能是基于用户交互更新某些模型,并且此模型也由诸如MyThread之类的工作线程处理.因此,他们必须正确地锁定这些物体.删除同步绝对会避免死锁,但其他错误会随着数据结构被不同步的并发访问所破坏而蔓延.
>应用程序不一定在EDT上执行长时间运行的操作.这是GUI应用程序的典型问题,但在这里似乎没有发生.我假设应用程序在大多数情况下工作正常,其中在EDT处理的事件抓住锁,更新某些东西,然后释放锁定.由于锁的持有者在美国东部时间(ICE)被锁死,因此无法获得锁定时出现此问题.
>将invokeAndWait更改为invokelater不是一个选项.操作程序表示这样做会导致其他问题.这并不奇怪,因为这种变化导致执行以不同的顺序发生,所以它将给出不同的结果.我会假设他们是不能接受的.

如果我们无法删除锁,并且我们无法更改为invokelater,那么我们可以安全地调用invokeAndWait.而“安全”是指在拨打之前放开锁.由于OP的申请的组织,这可能是任意的,但我认为这是唯一的办法.

我们来看看MyThread正在做什么.这很简单,因为堆栈上可能有一堆介入的方法调用,但从根本上来说,它是这样的:

synchronized (someObject) {
    // code block 1
    SwingUtilities.invokeAndWait(handler);
    // code block 2
}

当一些事件在处理程序前面的队列中潜移默化时,会出现此问题,该事件的处理需要锁定someObject.我们如何避免这个问题呢?您不能放弃同步块中的Java内置监视器锁之一,因此您必须关闭该块,进行调用并再次打开它:

synchronized (someObject) {
    // code block 1
}

SwingUtilities.invokeAndWait(handler);

synchronized (someObject) {
    // code block 2
}

如果someObject上的锁相当于调用堆栈从调用invokeAndWait中获取的可能是任意困难的,但我认为这样做的重构是不可避免的.

还有其他的陷阱.如果代码块2取决于由代码块1加载的某些状态,则该代码块2再次进行锁定时,该状态可能已经过期.这意味着代码块2必须从同步对象重新加载任何状态.它不能根据代码块1的结果进行任何假设,因为这些结果可能已经过时.

这是另一个问题.假设由invokeAndWait运行的处理程序需要从共享对象加载一些状态,例如,

synchronized (someObject) {
    // code block 1
    SwingUtilities.invokeAndWait(handler(state1,state2));
    // code block 2
}

您不能将invokeAndWait调用从同步块中迁移出来,因为这将需要不同步的访问获取state1和state2.您需要做的是在锁定期间将此状态加载到本地变量中,然后在释放锁定后使用这些本地进行调用.就像是:

int localState1;
String localState2;
synchronized (someObject) {
    // code block 1
    localState1 = state1;
    localState2 = state2;
}

SwingUtilities.invokeAndWait(handler(localState1,localState2));

synchronized (someObject) {
    // code block 2
}

在释放锁之后进行呼叫的技术称为公开呼叫技术.参见Doug Lea,Java并行编程(第2版),第2.4.1.3节. Goetz等人还对此技术进行了很好的讨论. Java并发实践,第10.1.4节.实际上,10.1节全面地涵盖了僵局;我推荐它很高.

总之,我相信使用上面描述的技术,或者在引用的书中,将会正确,安全地解决这个死锁问题.不过,我相信这需要进行很多细致的分析和重组.不过我看不到别的选择.

(最后我应该说,虽然我是甲骨文的雇员,但这并不是Oracle的官方声明.)

UPDATE

我想到了一些可能有助于解决问题的潜在重构.让我们重新考虑代码的原始模式:

synchronized (someObject) {
    // code block 1
    SwingUtilities.invokeAndWait(handler);
    // code block 2
}

这样按顺序执行代码块1,处理程序和代码块2.如果我们将invokeAndWait调用更改为invokelater,则处理程序将在代码块2之后执行.可以很容易地看到这将是应用程序的一个问题.相反,我们如何将代码块2移入invokeAndWait,以便它以正确的顺序执行,但仍然在事件线程上?

synchronized (someObject) {
    // code block 1
}

SwingUtilities.invokeAndWait(Runnable {
    synchronized (someObject) {
        handler();
        // code block 2
    }
});

这是另一种方法.我不知道处理程序传递给invokeAndWait是什么意思.但是可能需要invokeAndWait的一个原因是它从GUI中读取一些信息,然后使用它来更新共享状态.这必须在EDT上,因为它与GUI对象交互,并且invokelater不能使用,因为它将以错误的顺序发生.这意味着在进行其他处理之前调用invokeAndWait,以便将GUI中的信息读入临时区域,然后使用该临时区域继续处理:

TempState tempState;
SwingUtilities.invokeAndWait(Runnable() {
    synchronized (someObject) {
        handler();
        tempState.update();
    }
);

synchronized (someObject) {
    // code block 1
    // instead of invokeAndWait,use tempState from above
    // code block 2
}

java – 涉及Swing和AWT-EventQueue的无响应线程的更多相关文章

  1. ios – 何时使用Semaphore而不是Dispatch Group?

    我会假设我知道如何使用DispatchGroup,为了解问题,我尝试过:结果–预期–是:为了使用信号量,我实现了:并在viewDidLoad方法中调用它.结果是:从概念上讲,dispachGroup和Semaphore都有同样的目的.老实说,我不熟悉:什么时候使用信号量,尤其是在与dispachGroup合作时–可能–处理问题.我错过了什么部分?

  2. ios – 当我的主线程阻塞时,如何获得断点/日志/增加的可见性?

    在对UI响应的永无止境的追求中,我想更多地了解主线程执行阻止操作的情况.我正在寻找某种“调试模式”或额外的代码,或钩子,或任何东西,从而我可以设置一个断点/日志/将被击中的东西,并允许我检查如果我的主要线程“自愿”用于I/O的块(或任何原因,真的),除了在循环结束时空闲.在过去,我已经使用循环观察器观察了跑步循环的时钟周期,这对于查看问题很有价值,但是在你可以检查的时候,为了做一个好主意,为时已晚

  3. ios – 如何在不阻塞主线程的情况下添加SCNNode?

    解决方法我不认为使用dispatchQueue可以解决这个问题.如果我替换其他任务而不是创建SCNNode它按预期工作,所以我认为问题与SceneKit有关.thisquestion的答案表明SceneKit有自己的私有后台线程,它将所有更改批量化.因此,无论我使用什么线程来创建我的SCNNode,它们都会在与渲染循环相同的线程中的同一队列中结束.我正在使用的丑陋的解决方法是在SceneKit的委托渲染器方法中一次添加一些节点,直到它们全部完成.

  4. 在Swift中应用Grand Central Dispatch(上

    在这两篇教程中,你会学到GCD的来龙去脉。起步libdispatch是Apple所提供的在IOS和OSX上进行并发编程的库,而GCD正是它市场化的名字。Swift中的闭包和OC中的块类似甚至于他们几乎就是可交换使用的。但OC中的块可以安全的替换成Swift中的闭包。再一次,这完全取决于GCD。QoS等级表示了提交任务的意图,使得GCD可以决定如何制定优先级。QOS_CLASS_USER_INteraCTIVE:userinteractive等级表示任务需要被立即执行以提供好的用户体验。

  5. 在Swift中应用Grand Central Dispatch 下

    通过使用dispatch_barrrier和dispatch_sync,你做到了让PhotoManager单例在读写照片时是线程安全的。还有,使用dispatch_async异步执行cpu密集型任务,从而为视图控制器初始化过程减负。幸运的是,dispatchgroups就是专为监视多个异步任务的完成情况而设计的。调度组调度组在一组任务都完成后会发出通知。在组内所有事件都完成时,GCDAPI提供了两种方式发送通知。打开PhotoManager.swift,替换downloadPhotosWithComple

  6. swift详解之十六-----------GCD基础部分

    当你了解了调度队列如何为你自己代码的不同部分提供线程安全后,GCD的优点就是显而易见的。这完全取决于GCD。这个队列就是用于发生消息给UIView或发送通知的。GCD的“艺术”归结为选择合适的队列来调度函数以提交你的工作。

  7. Swift线程安全详解-概念,三种锁,死锁,Atomic,synchronized

    研究了下,是线程安全问题。UIKit以及Fundation事实上,大多数Cocoa提供的Api都不是线程安全的,尤其是与UI相关的UIKit,只能在主线程上操作。需要线程安全的时候,开发者自己维护就可以了。用来加锁,解锁。关于ObjectiveC参考这篇文章Atomic一个非Atomic的属性在非ARC的时候像这样可以看到,如果在多线程同时set的情况下,可能会造成release两次。Property的Runtime对应的C代码为可以看到,如果是nonatomic的,synchronized可以看看这个S

  8. Realm Swift

    一旦带有主键的对象被添加到Realm之后,该对象的主键将不可修改。IgnoredProperties重写Object.ignoredProperties()可以防止Realm存储数据模型的某个属性。Realm将不会干涉这些属性的常规操作,它们将由成员变量提供支持,并且您能够轻易重写它们的setter和getter。所有的查询在Realm中都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。

  9. 同步和异步

    如果是同步操作,它会阻塞当前线程并等待Block中的任务执行完毕,然后当前线程才会继续往下运行。并行队列中的任务根据同步或异步有不同的执行方式。同步执行异步执行串行队列当前线程,一个一个执行其他线程,一个一个执行并行队列当前线程,一个一个执行开很多线程,一起执行创建队列:主队列:这是一个特殊的串行队列。传入disPATCH_QUEUE_CONCURRENT表示创建并行队列。

  10. 【swift Objective-c】深入了解 核心比较语言特性

    ###OC与swiftautoreleasepoolsynchronizedappledoc关于线程安全objc_sync源码

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部