2

A次の特性を持つサードパーティ クラスを考えてみましょう。

  1. その実装のほとんどは非公開で非常に複雑であるため、拡張することはできますが、合理的に変更することはできません。

  2. A不必要に呼び出すべきではない非常に高価なコンストラクターがあります-私の場合、後で使用するためにデータの巨大なルックアップテーブルを構築します。

    これは、デコレータ クラスを のドロップイン置換として作成することAは問題外であることを意味します。デコレータは、そのコンストラクタから継承しAて呼び出す必要があるためです。

  3. Aはサードパーティ コードによって使用されるため、代わりにプロキシ クラスを使用することはできません。他のコードは のインスタンスを想定Aしており、それを取得する必要があります。

  4. A大量のレポートを生成するために多くのwrableをfrabbleする、非常に高価なレポート メソッドを多数提供します。

    これらのメソッドは、ローカルで使用される結果を生成しません。理論的には非同期である可能性がありますが、残念ながらそうではありません。

  5. Aお互いを呼び出す方法- たくさん。

  6. 実装は不透明で、常に流動的です。Aリフレクション ポスト インスタンス化を使用して物事をスリム化するなどのハックは問題外です。

  7. Aスレッドセーフではありません-実際には...

私は自分のプロジェクトでボトルネックになるポイントに達したので、実際のワラブルフラブリングを別のスレッドにプッシュすることを望んAで子クラスを作成しました。オブジェクトを単一スレッドのエグゼキュータ サービスに送信するためだけに、すべてのパブリック メソッドをオーバーライドしました。次に、それぞれがワーカー スレッドから適切なメソッドを実行します。BARunnableRunnablesuper

残念ながら、独自の public メソッドを頻繁に呼び出しAます。通常は問題ありませんが、私の場合、スーパークラスはからメソッドを呼び出しています。メソッドは実際の実行を延期し、新しいタスクをエグゼキューター サービスに送信するため、パフォーマンスと正確性の両方の問題が発生するため、これ大きな問題です。BB

Thread.currentThread()私の解決策は、すべてのメソッドの結果をチェックしてB、ワーカー スレッドからの呼び出しがsuper新しいタスクとして送信されるのではなく、メソッドに直接委任されるようにすることでした。

だから今、私はそれThread.currentThread()がパフォーマンスの問題になっているところにいます。おそらく、あまりにも頻繁に呼び出されるネイティブメソッドであるためです。

  • スーパークラスやワーカー スレッドからスレッドセーフな方法でメソッド呼び出しを確認するより高速な方法はありますか?

  • Aぐらつきを別のスレッドにプッシュできるようにする代替設計はありますか? Aサードパーティのコードであるため、ほとんど閉じ込められているようです...

4

3 に答える 3

1

スーパークラスやワーカー スレッドからスレッドセーフな方法でメソッド呼び出しを確認するより高速な方法はありますか?

ThreadLocalThread.currentThreadに置き換えることができます。たとえば、toを設定するだけで、設定されている場合 (メソッドを入力するときに確認してください)、新しいスレッドを生成せずに、同じスレッドで実行します。 BooleanThreadLocal

更新
ハッシュマップを使用して、メソッド呼び出しの追跡を維持します。メソッドに入ると、ハッシュマップに存在するかどうかを確認します (メソッドの名前をキーとして)。
存在しない場合は、ハッシュマップに入れ、バックグラウンド スレッドを使用してメソッドを呼び出します。
存在する場合は、すでにバックグラウンド スレッドにいる必要があります。同じスレッドで呼び出しを行う

于 2013-02-10T18:09:57.320 に答える
0

ランタイム環境でカスタム クラス ローダーの使用が許可されている場合、サードパーティ クラス A を、リフレクションを使用して元のクラス A を呼び出すカスタム デリゲート クラスに置き換えることができるはずです。

デリゲート クラス A の例を次に示します。

package fi.test;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassA {
    private static final Class<?> clazz;
    private static final Method doSomething;
    private Object instance;

    static {
        ClassLoader classLoader = new ClassLoader(ClassA.class.getClassLoader()) {
            @Override
            public synchronized Class<?> loadClass(String name)
                    throws ClassNotFoundException {
                if (!name.equals(ClassA.class.getName())) {
                    return super.loadClass(name);
                }
                Class<?> clazz = findLoadedClass(name);
                if (clazz == null) {
                    // The original class A is stored as ClassA.class.orig
                    InputStream inputStream = getParent().getResourceAsStream(
                            "fi/test/ClassA.class.orig");
                    try {
                        byte[] buffer = new byte[100000];
                        int length = inputStream.read(buffer);
                        clazz = defineClass(name, buffer, 0, length);
                    } catch (IOException exception) {
                        throw new ClassNotFoundException("", exception);
                    } finally {
                        try {
                            inputStream.close();
                        } catch (IOException exception) {
                        }
                    }
                }
                return clazz;
            }
        };
        try {
            // Class A
            clazz = classLoader.loadClass(ClassA.class.getName());
            // Do something
            doSomething = clazz.getMethod("doSomething", new Class<?>[0]);
        } catch (Exception exception) {
            throw new Error(exception);
        }
    }

    public ClassA() {
        try {
            instance = clazz.newInstance();
        } catch (InstantiationException exception) {
            throw new RuntimeException(exception);
        } catch (IllegalAccessException exception) {
            throw new RuntimeException(exception);
        }

    }

    public void doSomething() {
        try {
            doSomething.invoke(instance, new Object[0]);
        } catch (IllegalAccessException exception) {
            throw new RuntimeException(exception);
        } catch (InvocationTargetException exception) {
            throw new RuntimeException(exception);
        }
    }
}

これは元のクラス A です。

package fi.test;

public class ClassA {
    public void doSomething() {
        System.out.println("This is magic!");
    }
}

これは、デリゲートのテストに使用したクラスです。

package fi.test;

public class Tester {
    public static void main(String[] args) {
        ClassA instance = new ClassA();
        instance.doSomething();
    }
}
于 2013-02-10T22:32:43.397 に答える
0

Aスレッドセーフではないため、異なるスレッドで呼び出すことは安全ではありません。

アイデア:

  • のインスタンスをプールすることができますA。つまり、スレッドが A を必要とするときはいつでも、プールから 1 つを取得 (またはプールが空の場合は作成) し、それを使用してからプールに戻します。そうすれば、同時に必要な数の A のインスタンスを持つことができます。
  • インスタンス間で「巨大なルックアップ テーブル」をキャッシュするようにパッチを適用できAます (もちろん、それにはスレッド セーフである必要がありますが、テーブルが構築後にのみ読み取られる場合、それは自明のケースです)、Ainの新しいインスタンスを使用します。すべてのスレッド。
于 2013-02-10T18:09:09.413 に答える