web-dev-qa-db-ja.com

スレッドの制限

私はJava実行中の並行性であり、スレッド制限の概念と混同されています。

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

では、オブジェクトがスレッドに限定されている場合、他のスレッドはそのオブジェクトにアクセスできませんか?それは、スレッドに限定されることの意味ですか?オブジェクトをスレッドに限定するにはどうすればよいですか?

編集:しかし、オブジェクトを別のスレッドと共有したい場合はどうなりますか?スレッドAがオブジェクトOで終了した後、スレッドBがOにアクセスしたいとします。この場合、Aが終了した後も、OをBに限定できますか?

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

39
denniss

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

いいえ、その逆です。他のスレッドがオブジェクトにアクセスできないようにすると、そのオブジェクトは単一のスレッドに限定されていると言われます。

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

例:オブジェクトへのonly参照がローカル変数からのものである場合、他のスレッドはローカル変数にアクセスできないため、オブジェクトはdefinitely単一のスレッドに制限されます。

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

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

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

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

40
Joachim Sauer

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

_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仕様は、オブジェクトが常に制限されていることを静的に保証しています。したがって、制限を確実にするのは、実行時の条件や使用方法ではなく、Java仕様です。

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

つまり、最初の例では、JVMは、calledByMultipleThreads()(他のどのメソッドが_SomeClass.map_をいじっているのかを知っている)の内部を見ただけでは、オブジェクトがスレッド内に閉じ込められているかどうかを確認できません。 )。後者の例では、それが可能です。


編集:しかし、オブジェクトを別のスレッドと共有したい場合はどうなりますか?スレッドAがオブジェクトOで終了した後、スレッドBがOにアクセスしたいとします。この場合、Aが終了した後も、OをBに限定できますか?

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

10
Enno Shioji

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

これがスレッドの制限の意味です。オブジェクトにアクセスできるのは1つのスレッドだけです。

それは、スレッドに限定されることの意味ですか?

上記を参照。

オブジェクトをスレッドに限定するにはどうすればよいですか?

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

  • 新しいオブジェクトを作成し、
  • オブジェクトの参照をインスタンスまたはクラス変数に割り当てることは決してありません。
  • 参照用にこれを行うメソッドを呼び出すことはありません。
  • その後、オブジェクトはスレッドに制限されます。
6
Stephen C

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

簡単な例:

public String s;

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

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

5
Andreas_D

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

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

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

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

参考資料

3
dogbane

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

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

0
Bohemian

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

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

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

0
dbf

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

0
Grooveek