44

私はJava Concurrency in Practiceを読んでいて、スレッドの制限の概念と混同しています。その本はそう言っている

オブジェクトがスレッドに限定されている場合、限定されたオブジェクト自体がそうでない場合でも、そのような使用法は自動的にスレッドセーフになります。

オブジェクトがスレッドに限定されている場合、他のスレッドはそのオブジェクトにアクセスできませんか? 糸に縛られるとはそういうことか。オブジェクトをスレッドに閉じ込めておくにはどうすればよいでしょうか?

編集: しかし、オブジェクトを別のスレッドと共有したい場合はどうすればよいですか? スレッド A がオブジェクト O の処理を​​終えた後、スレッド B が O にアクセスしたいとします。

ローカル変数の使用は確かに一例ですが、それはオブジェクトを他のスレッドと共有しないことを意味します (AT ALL)。JDBC接続プールの場合、スレッドがその接続で完了すると、あるスレッドから別のスレッドに1つの接続を渡しませんか(JDBCを使用したことがないため、これについてはまったくわかりません)。

4

8 に答える 8

49

オブジェクトがスレッドに限定されている場合、他のスレッドはそのオブジェクトにアクセスできませんか?

いいえ、逆です。他のスレッドがオブジェクトにアクセスできないことを確認した場合、そのオブジェクトは単一のスレッドに限定されていると言われます。

オブジェクトを単一のスレッドに限定する言語レベルまたは JVM レベルのメカニズムはありません。オブジェクトへの参照が、別のスレッドがアクセスできる場所に逃げないようにする必要があります。クラスなど、参照のリークを回避するのに役立つツールはありますが、参照がどこにもリークしないことを保証するツールはありません。ThreadLocal

たとえば、オブジェクトへの唯一の参照がローカル変数からのものである場合、他のスレッドは決してローカル変数にアクセスできないため、オブジェクトは確実に 1 つのスレッドに限定されます。

同様に、オブジェクトへの唯一の参照が、単一のスレッドに限定されていることがすでに証明されている別のオブジェクトからのものである場合、その最初のオブジェクトは同じスレッドに限定されます。

Ad Edit:実際には、その存続期間中に一度に 1 つのスレッドのみがアクセスするオブジェクトを持つことができますが、その単一のスレッドが変更されます (Connection接続プールの JDBC オブジェクトが良い例です)。

ただし、そのようなオブジェクトが単一のスレッドによってのみアクセスされることを証明することは、オブジェクトがその存続期間全体にわたって単一のスレッドに限定されていることを証明することよりもはるかに困難です。

そして、私の意見では、これらのオブジェクトは実際には「単一のスレッドに限定される」ことはなく(これは強力な保証を意味します)、「一度に単一のスレッドでのみ使用される」と言えます。

于 2011-06-07T07:35:37.150 に答える
11

最も明白な例は、スレッド ローカル ストレージの使用です。以下の例を参照してください。

class SomeClass {
    // This map needs to be thread-safe
    private static final Map<Thread,UnsafeStuff> map = new ConcurrentHashMap<>();

    void calledByMultipleThreads(){
        UnsafeStuff mystuff = map.get(Thread.currentThread());
        if (mystuff == null){
            map.put(Thread.currentThread(),new UnsafeStuff());
            return;
        }else{
            mystuff.modifySomeStuff();
        }
    }
}

オブジェクト自体は、実行時にマップのメソッドに渡す代わりに他のスレッドを渡すと、他のスレッドに属するオブジェクトを取得するUnsafeStuffという意味で、他のスレッドと「共有できます」 。しかし、あなたはそうしないことを選択しています。これが「スレッドに限定された使い方」です。つまり、実行時の条件は、オブジェクトが異なるスレッド間で実際に共有されないようになっているということです。 Thread.currentThread()get

一方、以下の例では、オブジェクトは自動的にスレッドに限定され、いわば「オブジェクト自体」がスレッドに限定されます。これは、実行時の状態に関係なく、他のスレッドから参照を取得できないという意味です。

class SomeClass {
    void calledByMultipleThreads(){
        UnsafeStuff mystuff = new UnsafeStuff();
        mystuff.modifySomeStuff();
        System.out.println(mystuff.toString());
    }
}

ここで、UnsafeStuffはメソッド内で割り当てられ、メソッドが戻るとスコープ外になります。つまり、Java 仕様は、オブジェクトが常に 1 つのスレッドに限定されることを静的に保証します。したがって、制限を保証するの はランタイム条件や使用方法ではなく、Java 仕様です。

実際、最新の JVM は、最初の例とは異なり、そのようなオブジェクトをスタックに割り当てることがあります (個人的には確認していませんが、少なくとも現在の JVM ではそうではないと思います)。

しかし、言い換えれば、最初の例では、JVM はオブジェクトがスレッド内に閉じ込められているかどうかを確認することができませcalledByMultipleThreads()SomeClass.map。後者の例では、それが可能です。


編集:しかし、オブジェクトを別のスレッドと共有したい場合はどうすればよいですか? スレッド A がオブジェクト O の処理を​​終えた後、スレッド B が O にアクセスしたいとします。

この場合は「閉じ込められた」とは呼ばないと思います。これを行うと、オブジェクトが同時にアクセスされないようにするだけです。これが、EJB 並行処理のしくみです。問題の共有オブジェクトをスレッドに「安全に公開」する必要があります。

于 2011-06-07T07:55:20.250 に答える
6

オブジェクトがスレッドに限定されている場合、他のスレッドはそのオブジェクトにアクセスできませんか?

これがスレッドの制限の意味です。オブジェクトは、1 つのスレッドからしかアクセスできません。

糸に縛られるとはそういうことか。

上記を参照。

オブジェクトをスレッドに閉じ込めておくにはどうすればよいでしょうか?

一般的な原則は、別のスレッドが参照できる場所に参照を配置しないことです。これを保証する一連のルールを列挙するのは少し複雑ですが、(たとえば)

  • 新しいオブジェクトを作成し、
  • オブジェクトの参照をインスタンス変数またはクラス変数に割り当てない。
  • 参照のためにこれを行うメソッドを呼び出すことはありません。
  • その場合、オブジェクトはスレッドに限定されます。
于 2011-06-07T07:36:11.723 に答える
5

それが言いたいのでしょうね。メソッド内でオブジェクトを作成し、run他のインスタンスへの参照を渡さないのと同じです。

簡単な例:

public String s;

public void run() {
  StringBuilder sb = new StringBuilder();
  sb.append("Hello ").append("world");
  s = sb.toString();
}

StringBuilder インスタンスは、(この run メソッドを実行する) スレッドに限定されているため、スレッドセーフです。

于 2011-06-07T07:36:25.527 に答える
3

1 つの方法は、オブジェクトがスレッドのスタックに限定されたローカル変数であるため、他のスレッドがアクセスできない「スタック限定」です。以下のメソッドでは、listはローカル変数であり、メソッドからエスケープしません。リストは実行中のスレッドのスタックに限定されるため、スレッドセーフである必要はありません。他のスレッドはそれを変更できません。

public String foo(Item i, Item j){
    List<Item> list = new ArrayList<Item>();
    list.add(i);
    list.add(j);
    return list.toString();
}

オブジェクトをスレッドに限定するもう 1 つの方法は、ThreadLocal各スレッドが独自のコピーを持つことを可能にする変数を使用することです。以下の例では、各スレッドが独自のオブジェクトを持っているため、複数のスレッドからアクセスされないため、スレッドセーフDateFormatではないという事実について心配する必要はありません。DateFormat

private static final ThreadLocal<DateFormat> df
                 = new ThreadLocal<DateFormat>(){
    @Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyyMMdd");
    }
  };

参考文献

于 2011-06-07T07:41:50.760 に答える
0

それがまさにそれが意味することです。オブジェクト自体は1つのスレッドによってのみアクセスされるため、スレッドセーフです。ThreadLocalオブジェクトは、唯一のスレッドにバインドされる一種のオブジェクトです

于 2011-06-07T07:36:45.607 に答える
0

参照: http://codeidol.com/java/java-concurrency/Sharing-Objects/Thread-Confinement/

スレッドの制限を維持するためのより正式な手段は ThreadLocal です。これにより、スレッドごとの値を値保持オブジェクトに関連付けることができます。Thread-Local は、値を使用するスレッドごとに個別の値のコピーを保持する get および set アクセサー メソッドを提供するため、get は、現在実行中のスレッドから set に渡された最新の値を返します。

1 つのスレッドごとにオブジェクトのコピーを保持します。スレッド A はスレッド B のコピーにアクセスできず、特別に行う場合は不変条件を破ります (たとえば、ThreadLocal 値を静的変数に割り当てるか、他の方法を使用して公開します)。

于 2011-06-07T07:35:28.613 に答える
0

つまり、1 つのスレッドで実行されているコードのみがオブジェクトにアクセスします。

この場合、オブジェクトは「スレッドセーフ」である必要はありません。

于 2011-06-07T07:43:30.040 に答える