⬆️ ⬇️

"Dancing with a tambourine" around Thread



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



Accordingly, the issue of control over the generation, life and use of the resources of the platform by the child threads created from scripting has become an “edge”. Scripting streams can be created using all available methods. In GoogleAppEngine, the problem of child threads was solved by simply banning their generation. In our case, I want to have a more flexible solution. Therefore, it is necessary to 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 the expectations were not met.

')

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 stream that spawned the current thread. For a long time, searching for information in the internet about the options for implementing getting a link to the parent stream (or a list of children), exactly zero solutions were found.



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



Decision



To solve the problem, the following path was chosen: JDK6 allows you to connect javaagent on the fly. After connecting the javaagent, we override 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 inheritance hierarchy. You can only change the body of the methods, the structure of the class should not change.

Those. we need to attach the link to the parent stream to the field already defined in the source class! We look in java.lang.Thread - and about a miracle! inside there is a private thread threadQ; which is not used anywhere in the class body. Hmm, as if Java developers left this field for us :).



Solution in code



// 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 for the Agent class, which is used in the example.



PS: In the depths of consciousness somewhere there is a thought that there is some simple way to solve the described problem and it is so trivial that nobody writes anything about it on the Internet.



UPD1 : There are concerns that the thredQ variable is used in the JVM native code, then an alternative would be to create your own stream manager (singleton) in which to create information about interconnections when creating a new thread (you must also override the init method Thread class).



UPD2 : apple_fan brought a very interesting solution , for which he thanks. See the comment below about the limitations of this solution.



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



UPD4 : Throwable offers its own solution , I think a very great option, it was supposed to go along this path initially.

Source: https://habr.com/ru/post/74208/



All Articles