9

私のWebアプリケーションには、バックグラウンドサービスがあります。このサービスは、Engineクラスを含み、ExecutorService複数のスレッドを使用するように構成され、GeneratorTasksを受け入れるGeneratorクラスを使用します。

@Component
public class Generator {
    @Autowired
    private Engine heavyEngine;

    private ExecutorService exec = Executors.newFixedThreadPool(3);

    //I actually pass the singleton instance Generator class into the task.
    public void submitTask(TaskModel model, TaskCallback callback) {
        this.exec.submit(new GeneratorTask(model, this, callback));
    }
}

@Component
public class Engine {
    public Engine() {
        //time-consuming initialization code here
    }
}

public class GeneratorTask implements Callable<String> {
    public GeneratorTask(TaskModel m, Generator g, ReceiptCallback c) {
        this.m = m;
        this.generator = g;
        this.c = c;
    }

    public String call() throws Exception {
        //This actually calls the Engine class of the generator.
        //Maybe I should have passed the Engine itself?
        this.generator.runEngine(c);  
    }
}

Engineクラスは初期化に時間がかかるので、理想的にはスレッドごとに1回だけ初期化したいと思います。インスタンスは複数のスレッド間で共有できないため、シングルトンインスタンスにすることはできません(順次処理に依存しています)。ただし、処理タスクが完了した後、インスタンスを再利用することはまったく問題ありません。

private Engine heavyEngine変数をThreadLocal変数にすることを考えていました。ただし、私はSpringも初めてなので、Springアノテーションを使用してThreadLocal変数を挿入する別の方法があるのではないかと考えていました。Beanのスコープを指定することを検討しましたがrequest、設計上、どのように処理すればよいかわかりません。

私のデザインを改善する方法についてのガイダンスをいただければ幸いです。

4

3 に答える 3

12

まず第一に、放棄しますThreadLocal-そのクラスには何か怖いものがあります。必要なのはオブジェクトプーリングだけです。これはよく知られた機能ではありませんが、Springはこれもサポートしています。

<bean id="engineProto" class="Engine" scope="prototype" lazy-init="true"/>

<bean id="engine" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource">
        <bean class="org.springframework.aop.target.CommonsPoolTargetSource">
            <property name="targetClass" value="Engine"/>
            <property name="targetBeanName" value="engineProto"/>
            <property name="maxSize" value="3"/>
            <property name="maxWait" value="5000"/>
        </bean>
    </property>
</bean>

これで、を挿入すると、実際には、プール内の解放オブジェクトへのすべての呼び出しを委任するengineプロキシオブジェクト(インターフェイスが必要)を受け取ります。Engineプールサイズは構成可能です。もちろん、 CommonsPoolの代わりにThreadLocalTargetSourceどちらを使用するかを妨げるものは何もありません。どちらのアプローチも、への排他的でスレッドセーフなアクセスを保証します。ThreadLocalEngine

最後に、プーリングを手動で使用するか(ただし、上記のソリューションの利点は、完全に透過的であるということです)、定義によってプールされるEJBに切り替えることができます。

于 2012-09-27T15:58:11.740 に答える
5

参考までに、Spring 3.0以降には、スレッドでバックアップされたScope実装であるSimpleThreadScopeが含まれています。

これを使用するには、カスタムスコープを登録する必要があります。

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="thread">
                <bean class="org.springframework.context.support.SimpleThreadScope" />
            </entry>
        </map>
    </property>
</bean>

次に、スレッドスコープのBeanを宣言します。

<bean id="myBean" class="com.foo.MyBean" scope="thread">
    ...
</bean>
于 2014-11-12T21:13:52.983 に答える
1

私はのためのファクトリを作成し、Engineそれを内部と呼んでいたでしょうGeneratorTask。このようにして、heavyEngine内部のフィールドGeneratorと。内のGeneratorコンストラクター引数を削除できますGeneratorTask
次に、の初期化時間を節約したい場合Engineでも、シングルトンとして宣言できますが、synchronizedスレッドセーフではないメソッドでキーワードを使用します。

public class Generator {    
    @Autowired private EngineFactory engineFactory;
    private ExecutorService exec = Executors.newFixedThreadPool(3);

    public void submitTask(TaskModel model, TaskCallback callback) {
        this.exec.submit(new GeneratorTask(engineFactory, model, callback));
    }
}

public class EngineFactory {
    @Autowired private Engine instance;

    public Engine getInstance() {
        return instance;
    }
}

public class Engine {
    public Engine() {
        //time-consuming initialization code here
    }

    public synchronized void runEngine() {
        // Do non thread safe stuf
    } 
}

public class GeneratorTask implements Callable<String> {
    public GeneratorTask(EngineFactory f, TaskModel m, ReceiptCallback c) {
        this.f = f;
        this.m = m;
        this.c = c;
    }

    public String call() throws Exception {
        Engine engine = f.getInstance();
        engine.runEngine();
        ... 
    }
}

エンジンをCallableに渡す純粋なSpringの方法はおそらくありますが、この場合、ファクトリーは私の意見では十分です。

于 2012-09-27T04:08:17.737 に答える