2

重複の可能性:
Java: コンストラクターでスレッドを開始しないのはなぜですか? 終了するには?

バグや悪い習慣を見つけるために、自分のコードでFindBugsを実行することに慣れています。今日、クラスコンストラクターでスレッドを開始しているという事実に文句を言います。

本当に悪いことですか?理由を説明していただけますか?

クラスが final である場合、少なくとも安全ですか?

編集

スレッドは内部クラスとして実装され、開始時にすでに初期化されているメイン クラスのフィールドのみを使用します。

public final class SingletonOuter {
    private static SingletonOuter ourInstance = new SingletonOuter();

    public static SingletonOuter getInstance() {
        return ourInstance;
    }

    private final SomeOtherClass aField;

    private SingletonOuter() {
        aField=new SomeOtherClass(); 
        thread=new InnerThread();
        thread.start();
    }

    private boolean pleaseStop;

    private synchronized boolean askedStop(){return pleaseStop;}
    public synchronized void stop(){
        pleaseStop=true;  
    }

    private final InnerThread thread ;
    private class InnerThread extends Thread{
        @Override public void run() {
            //do stuff with aField until askedStop()
        }
    }

}

編集

最後にスレッドの開始を getInstance メソッドに移動して、将来のバグが発生する可能性を回避します。

public final class SingletonOuter {
        private static SingletonOuter ourInstance

        public static SingletonOuter getInstance() {
            if (ourInstance==null){
                ourInstance= = new SingletonOuter();
                ourInstance.thread.start();
            }

            return ourInstance;
        }

        private final SomeOtherClass aField;

        private SingletonOuter() {
            aField=new SomeOtherClass(); 
            thread=new InnerThread();

        }
        ...
4

3 に答える 3

5

コンストラクターで新しいスレッドを作成するのが悪い習慣なのはなぜですか?

Findbugs は、オブジェクト構築に関する命令の並べ替えの可能性に関する問題を警告しています。新しいオブジェクト用のメモリ空間が割り当てられますが、開始時までにフィールドのいずれかが初期化されているという保証はありませんInnerThread。コンストラクターが終了する前にフィールドが初期化されますが、(たとえば) 開始時に使用を開始した場合、初期化されるという保証はfinalありませInnerThreadaField。Java コンパイラは、パフォーマンス上の理由からこれを行います。また、コンストラクターによって新しいインスタンスが返された に非最終フィールドの初期化を移動するオプションもあります。

コンストラクターで新しいスレッドを開始すると、スレッドが部分的に初期化されたオブジェクトを処理する可能性があります。これthread.start()がコンストラクターの最後のステートメントであっても、新しいスレッドは、並べ替えのために部分的に構築されたオブジェクトにアクセスしている可能性があります。これは Java 言語仕様の一部です。

トピックに関する適切なリンクは次のとおりです。独自のコンストラクター内で thread.start() を呼び出す

それは次のことを言及しています:

コンストラクター内から開始すると、Java メモリー モデルのガイドラインに違反することが保証されます。詳細については、 Brian Goetz の Safe Construction Techniquesを参照してください。

編集:

コードは にアクセスしている新しいスレッドを開始しているため、 Java メモリ モデルafieldによると、スレッドの実行開始時に が適切に初期化される保証はありません。afield

代わりstart()に、クラスにメソッドを追加してthread.start(). これはより良い方法であり、このクラスを使用している他のクラスに対して、コンストラクターでスレッドが作成されていることをより明確に示します。

于 2012-05-25T17:18:39.470 に答える
2

一般に、コンストラクターで行うことは慎重に行うのが最善です。

オブジェクトはまだ無効な状態にあるため、誰もアクセスできないようにします。コンストラクターからスレッドを開始すると、構築中のオブジェクトへの参照が含まれている可能性があります (そうでない場合、なぜコンストラクターはそれを開始するのでしょうか?)。この参照は、スレッドの開始時と有効になる直後に無効なオブジェクトを指します。そこでは、恐ろしい競合状態がすぐに発生する可能性があります。

これについての良い記事へのリンクは次のとおりですhttp://www.ibm.com/developerworks/java/library/j-jtp0618/index.html

于 2012-05-25T17:18:53.700 に答える
0

そのクラスをインスタンス化するたびに、スレッドを作成します。スレッドはコストが高く、テストが非常に困難です。パフォーマンスの問題が発生する多くのオブジェクトをインスタンス化する場合は、ThreadPool を使用してスレッド数の制限を修正することを検討する必要があります。また、スレッドで発生している動作を単体テストしようとすると問題が発生する場合。

于 2012-05-25T17:18:22.577 に答える