「タンバリンと踊る」スレッドの周り



クラウドWebアプリケーションプラットフォームを開発する際、他のプラットフォームサービスのより柔軟な管理のために、 javaスクリプト技術に基づいてサーバーロジックサービスが実装されました。



したがって、「エッジになった」スクリプトから作成された子ストリームによるプラットフォームリソースの生成、寿命、および使用に対する制御の問題。 スクリプトを使用したスト​​リームは、利用可能なすべての方法で作成できます。 GoogleAppEngineでは、子スレッドの生成を単に禁止することで、子スレッドの問題を解決しました。 私たちの場合、より柔軟なソリューションが必要です。 したがって、メイン要求スレッドから作成された子スレッドを制御する必要があります。



最初は、タスクは簡単であり、Javaにはこのための標準ツールがあると想定されていました。 しかし、期待は実現しませんでした。



Javaの標準ツールはこれを許可しません! 奇妙ですが、何らかの理由でjava.lang.Threadクラスに、現在のスレッドを生成した親スレッドへのリンクまたはリンクがありません。 親ストリーム(または子のリスト)へのリンクを取得するための実装オプションに関するインターネット上の情報を長時間検索した結果、まったく解決策が見つかりませんでした。



ソースコードjava.lang.Threadに登ります...分析後、クラスには実際に親子関係がないことが明らかになりました。 どうする 自然に不足しているものを変更します。



解決策



この問題を解決するために、次のパスが選択されました。JDK6では、 javaagentをその場で接続できます。 javaagentを接続した後、Threadクラスを再定義し、親への参照を追加します。 ただし、制限があります。クラスの再定義では、新しいフィールドやメソッドを追加、削除したり、メソッドや継承階層のシグネチャを変更したりしてはいけません。 変更できるのはメソッドの本体のみで、クラス構造は変更しないでください。

つまり ソースクラスで既に定義されているフィールドに親ストリームへのリンクを埋め込む必要があります。 私たちはjava.lang.Threadを見て、そして見よ! 内部にはプライベートスレッドthreadQフィールドがあります。 クラスの本体のどこでも使用されていません。 うーん、あたかもJava開発者がこのフィールドを残してくれたかのように:)。



コードソリューション



// 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 .








javaagentとリフレクションを使用した決定により、JDK 1.6.XでのJava Instrumentation、ロード後のクラス再定義になるように促されました。 例で使用されているAgentクラスのソースコードもあります。



PS:意識の奥深くで、考えられていることはどこかに潜んでいて、説明された問題を解決する簡単な方法があり、インターネット上で誰もそれについて何も書いていない。



UPD1 :ネイティブJVMコードでthredQ変数が使用されるのではないかという懸念があります。その場合は、独自のスレッドマネージャー(シングルトン)を作成し、新しいスレッドを作成するときに関係に関する情報を表示します(Threadクラスのinitメソッドも再定義する必要があります)。



UPD2apple_fanは非常に興味深いソリューションを作成しました。彼に感謝します。 このソリューションの制限については、以下のコメントをご覧ください。



UPD3amosk別の解決策を提案しました。私の意見では、私の特定の問題を解決するのに最も合理的です。



UPD4Throwable独自のソリューションを提供ます。非常に優れたオプションだと思います。最初からそのように進むべきでした。



All Articles