"Dancing with a Tambourine" Around Thread


    When developing a cloud web application platform, a server logic service was implemented based on java scripting technology for more flexible management of other platform services.

    Accordingly, the issue of control over the generation, life and use of platform resources by child streams created from scripting “became an edge”. Streams through scripting can be created in all available ways. At GoogleAppEngine, the problem of child threads was resolved by simply prohibiting their generation. In our case, I want to have a more flexible solution. Therefore, you must have control over the child threads created from the main request thread.

    Initially, it was assumed that the task is trivial and that Java has standard tools for this. But expectations did not materialize.

    Standard tools in Java do not allow this! Strange, but for some reason in the java.lang.Thread class there is no link or link to the parent thread that generated the current thread. After a long search for information on the internet about the implementation options for obtaining links to the parent stream (or to the list of children), exactly zero solutions were found.

    We climb into the sources java.lang.Thread ... After the analysis, it became clear that there really are no parent-child relationships in the class. What to do? Naturally modify what is missing.

    Decision


    To solve the problem, the following path was chosen: JDK6 allows you to connect javaagent on the fly . After connecting javaagent, we redefine the Thread class, adding a reference to the parent to it. However, there are limitations: class redefinition should not add, delete new fields or methods, change the signature of methods or the inheritance hierarchy. Only the body of methods can be changed, the structure of the class should not change.
    Those. we need to enclose the link to the parent thread in the field already defined in the source class! We look in java.lang.Thread - and lo and behold! inside there is a private thread threadQ field; which is not used anywhere in the class body. Hmm, as if the Java developers left this field for us :).

    Code Solution


    //модифицируем Thread.class
    ClassPool pool = ClassPool.getDefault();
    CtClass ctClass = pool.get("java.lang.Thread");

    for (CtMethod ctMethod : ctClass.getDeclaredMethods()) {
     if (ctMethod.getName().equals("init")) {
       //дорабатываем метод init, сохраняем ссылку на текущий поток (родительский)
       ctMethod.insertBefore("threadQ = currentThread();");
       break;
     }
    }

    //загружаем javaagent
    String agentPath = "/path/javaageent.jar";

    VirtualMachineDescriptor vmd = ...;
    VirtualMachine vm = VirtualMachine.attach(vmd);

    vm.loadAgent(agentPath);

    //переопределяем Thread.class
    ClassDefinition classDef = new ClassDefinition(Thread.class, ctClass.toBytecode());
    Agent.redefineClasses(classDef);

    //получаем ссылку на поле класса
    Field parentThreadField = Thread.class.getDeclaredField("threadQ");
    parentThreadField.setAccessible(true);

    //создадим 10 дочених потоков
    for (int i = 0; i < 10; i++) {
     final int n = i;
     new Thread(new Runnable() {
      public void run() {
       try {
        Thread.sleep(10000);
        System.out.println("thred #" + n);
       } catch (Exception ex) {
       }
      }
     }).start();
    }

    Thread currentThread = Thread.currentThread();
    ThreadGroup threadGroup = currentThread.getThreadGroup();
    Thread[] threads = new Thread[threadGroup.activeCount()];
    threadGroup.enumerate(threads);
    for (Thread t : threads) {
      //достаем родительский поток используя parentThreadField.get(t)
      if (currentThread.equals(parentThreadField.get(t))) {
        System.out.println(t);
      }
    }


    * This source code was highlighted with Source Code Highlighter.


    Java Instrumentation with JDK 1.6.X, Class Re-definition after loading, came up with a solution using javaagent and reflection . There is also the source code of the Agent class, which is used in the example.

    PS: Somewhere in the back of my mind lurks the thought that there is some simple way to solve the described problem and it is so trivial that no one writes anything about it on the internet.

    UPD1 : There are fears that the thredQ variable is used in native JVM code, then an alternative would be to create your own thread manager (singleton), in which when creating a new thread, shove info about relationships (you also need to redefine the init method of the Thread class).

    UPD2 : apple_fan brought a very interesting solutionThank you for that. see the comment below about the limitations of this solution.

    UPD3 : amosk suggested another solution , in my opinion the most rational for solving my particular problem.

    UPD4 : Throwable offers its own solution , I think a very excellent option, it was supposed to go that way from the very beginning.

    Also popular now: