groovy中线程池的使用_使用Groovy和JMX在死锁中检测Java线程

本文介绍了如何使用Groovy结合Java的ThreadMXBean和JMX来检测Java应用程序中的死锁。通过示例展示了如何利用JConsole、VisualVM和命令行工具jstack来识别死锁线程,以及如何编写Groovy脚本来自动化死锁检测过程。
摘要由CSDN通过智能技术生成

groovy中线程池的使用

不幸的是,利用多个线程的Java应用程序有时可能会陷入可怕的死锁状态。 幸运的是,Java平台使死锁检测相对容易。 实际上,内置的(自J2SE 5起ThreadMXBean (通过JMX公开的PlatformManagedObject )使此信息可通过findDeadlockedThreads()findMonitorDeadlockThreads()方法供任何使用JMX的客户端使用。 通用的“ JMX客户端”(例如JConsoleVisualVM)使用它来提供有关检测到的死锁的信息,但是可以编写自定义工具和脚本来提供相同的详细信息。 在本文中,我将结合使用Groovy和Attach API来检测本地运行的JVM进程的死锁线程。

Java教程提供了一个简单而有趣的Java代码示例,该示例通常会在“ 基本类 ”线索的“ 并发 ”课程中的“ 死锁 ”页面上导致死锁。 我在这里仅以最细微的修改形式提供了该示例,因为我将针对其运行JMX客户端以检测死锁的代码。 (作为一个旁注,我一直在reddit / Java和其他在线论坛上看到一些帖子,它们寻求学习Java的最佳免费在线入门资源;我想不到比Java教程更好的答案。)

Deadlock.java(改编自Java教程

package dustin.examples.jmx.threading;

import static java.lang.System.out;

/**
 * Example of a class that often will lead to deadlock adapted from the <em>
 * Java Tutorials</em> "Concurrency" section on "Deadlock":
 * http://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html.
 */
public class Deadlock
{
   static class Friend
   {
      /** Friend's name. */
      private final String name;

      /**
       * Parameterized constructor accepting name for Friend instance.
       * @param newName Name of new Friend instance.
       */
      public Friend(final String newName)
      {
         this.name = newName;
      }
      
      /**
       * Provide this instance's name.
       * 
       * @return Friend's name.
       */
      public String getName()
      {
         return this.name;
      }

      /**
       * Bow from friend. Synchronized for thread-safe access.
       * 
       * @param bower Friend that is bowing.
       */
      public synchronized void bow(final Friend bower)
      {
         out.format("%s: %s has bowed to me!%n", 
            this.name, bower.getName());
         bower.bowBack(this);
      }

      /**
       * Bow back to friend who bowed to me. Synchronized for thread-safe access.
       * 
       * @param bower Friend who has bowed back to me.
       */
      public synchronized void bowBack(final Friend bower)
      {
         out.format("%s: %s  has bowed back to me!%n",
            this.name, bower.getName());
      }
   }

   /**
    * Simple executable function that demonstrates deadlock when two friends
    * are waiting on each other to bow to finish bowing.
    * 
    * @param arguments Command-line arguments: none expected.
    */
   public static void main(final String[] arguments)
   {
      final Friend alphonse = new Friend("Alphonse");
      final Friend gaston = new Friend("Gaston");
      new Thread(new Runnable()
      {
         public void run() { alphonse.bow(gaston); }
      }, "Gaston Bowing").start();
      new Thread(new Runnable()
      {
         public void run() { gaston.bow(alphonse); }
      }, "Alphonse Bowing").start();
   }
}

下一个屏幕快照显示,这个简单的应用程序在两个线程之间陷入了僵局。

即使我们不知道此应用程序旨在演示死锁,但它永远不会结束或更改状态这一事实很好地表明它已陷入死锁。 我们可以使用JMX来确定这一点。 无论我们是使用JMX客户端,jstack还是其他机制来确定死锁,我们都可能需要Java进程的pid(进程ID),因此下一个屏幕快照指示同时使用这两个jps (pid为3792)和jcmd (后者仅自JDK 7起可用)。

知道pid是3792后,我可以在命令行上运行“ jconsole 3794”,让JConsole加载有关该死锁Java进程的信息。 在这种情况下,我对线程感兴趣,下图显示了此死锁演示应用程序在JConsole中的“线程”选项卡。

紧邻的上一个屏幕快照带有两个感兴趣的线程。 在经过修改的代码示例中,我将它们命名为“ Gaston Bowing”和“ Alphonse Bowing”,以使其在JConsole和其他工具中更容易找到。 如果我没有在示例中提供这些显式名称(Java教程中的原始示例未提供),则它们将被更通用的名称“ Thread-0”和“ Thread-1”引用。 加载JConsole并提供“线程”选项卡后,我可以单击“检测死锁”按钮以查看接下来的两个屏幕快照(每个选定的两个线程各一个)中的信息。

通过查看每个已标识的死锁线程的JConsole输出,我们可以看到每个线程都被阻塞并在另一个线程上等待。 每个线程提供的堆栈跟踪还提供了非常清楚的文档,说明此死锁在代码中的位置。

可以使用jvisualvm命令启动Java VisualVM,然后可以选择下一个屏幕快照中所示的Java进程。 请注意,VisualVM会自动检测VisualVM的“ 线程 ”选项卡上的死锁,并以红色字体显示一条消息,指出这一点。

当我如图所示单击“线程转储”按钮以查看哪些线程死锁时, 提供线程转储 。 如下一个屏幕快照所示,该线程转储的底部是死锁线程的详细信息。

就像JConsole所做的那样,VisualVM还在提供的线程转储中指示两个弯曲线程正在互相等待,并且为每个标识为参与致命拥抱的线程提供了“ Java堆栈信息”。

JConsole和Java VisualVM使识别死锁中涉及的线程变得容易。 但是,当使用命令行工具而不是基于GUI的工具时, jstack是一个明显的选择。 再次使用先前为该特定进程标识的pid(在我的情况下为3792),可以简单地键入jstack 3792来查看该应用程序的线程转储。 接下来的两个屏幕快照显示了其中的一部分,第一个快照显示了正在运行的命令以及输出的开始,第二个快照显示了与死锁相关的部分。

不必太仔细观察就可以意识到jstack的输出与VisualVM的“线程转储”中提供的输出相同。

JConsole和VisualVM提供了一种很好的GUI方法来识别死锁,而jstack在命令行中也是如此。 有了这些工具,您可能会想知道为什么我们会关心能够构建自己的工具和脚本来做到这一点的原因。 通常,这些工具就足够了,但是有时候我可能想在获得信息后对信息做一些特定的事情,或者我希望答案比这些工具所提供的更为直接。 如果我需要以编程方式对信息进行处理,或者只是想要识别死锁中涉及的线程而没有其他线程的详细信息,则可以使用自定义工具。 这篇文章的其余部分集中于此。

下面的代码清单包含Groovy代码,用于打印与ThreadMXBean提供的与死锁线程相关的线程信息。 该代码利用了此处未显示的Groovy代码(使用Attach API的JmxServer ),该代码可以在我的上一篇博文中找到。

displayDetectedDeadlock.groovy

#!/usr/bin/env groovy
def pid = args[0]
def javaThreads = new javax.management.ObjectName("java.lang:type=Threading")
def server = JmxServer.retrieveServerConnection(pid)
long[] deadlockedThreadIds = server.invoke(javaThreads, "findDeadlockedThreads", null, null)
deadlockedThreadIds.each
{ threadId ->
   Object[] parameters = [threadId, 10]
   String[] signature = [Long.TYPE, Integer.TYPE]
   def threadInfo = server.invoke(javaThreads, "getThreadInfo", parameters, signature)
   print "\nThread '${threadInfo.threadName}' [${threadInfo.threadId}] is "
   print "${threadInfo.threadState} on thread '${threadInfo.lockOwnerName}' ["
   println "${threadInfo.lockOwnerId}]:\n"
   println "Java stack information for the threads listed above:"
   println "==================================================="
   println "'${threadInfo.threadName}':"
   threadInfo.stackTrace.each
   { compositeData ->
      print "\tat ${compositeData.className}.${compositeData.methodName}("
      println "${compositeData.fileName}:${compositeData.lineNumber})"
   }
   println "\n\n"
}

上面的Groovy脚本将10作为要提供的堆栈跟踪信息的最大深度,因为它提供的详细信息与JConsole,VisualVM和jstack中提供的信息类似。 当然,这种方法的优势在于,这里打印到标准输出的任何数据都可以在运行时决策中使用,并可以通过Java或Groovy代码以编程方式对其进行操作。 下一个屏幕快照显示了针对本文之前使用的死锁应用程序运行此脚本的输出。

Java使得检测锁定在致命死锁(称为死锁)中的线程变得容易。 尽管诸如JConsole,VisualVM和jstack之类的通用工具通常足以识别这些情况,但是能够编写自定义脚本和工具来完成相同的工作还是很不错的。 这使开发人员可以灵活地在其脚本和工具中直接包括死锁检测,而无需解析jstack输出或诉诸其他数据抓取方法。


翻译自: https://www.javacodegeeks.com/2013/04/detecting-java-threads-in-deadlock-with-groovy-and-jmx.html

groovy中线程池的使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值