2

スレッド セーフについて読んでいるときに、この問題に遭遇しました。私が正しければ、ローカルプリミティブとオブジェクト参照はスタック内に存在し、スタック内の参照によって指される実際のオブジェクトはヒープ内に存在します。

しかし、メソッド ローカルの非プリミティブ オブジェクトの初期化に関しては、同時実行の問題が発生しませんか? メソッド locals 非プリミティブがヒープに存在し、ポインターのみがスタックに存在する場合、それはインスタンス変数と同じではありませんか?

誰かがこれを理解するのを手伝ってくれませんか....

PS

それぞれが独自の 2 つのスタックと 1 つのヒープを持つ 2 つのスレッドを考えてみてください。私が理解したのは、2 つのスレッドがメソッドのローカル プリミティブ変数をスタック内に保持していることです。私はそれについて問題はありません。

しかし、非プリミティブ メソッド ローカル変数を持つメソッドがある場合はどうなるでしょうか。次に、その変数のオブジェクトがヒープ内に格納されている場合、両方のスレッドが同じオブジェクトにアクセスできますよね? その場合、同期の問題が発生します。

それが私が求めていることです。

ありがとう

4

5 に答える 5

3

しかし、非プリミティブ メソッド ローカル変数を持つメソッドがある場合はどうなるでしょうか。次に、その変数のオブジェクトがヒープ内に格納されている場合、両方のスレッドが同じオブジェクトにアクセスできますよね? その場合、同期の問題が発生します。

なぜ 2 つの参照が同じオブジェクトを参照すると考えるのでしょうか。

参照されるオブジェクトの作成は、 new (または他の同様の方法ですが、考え方は同じです) によって明示的に行われます。

したがって、C++ とは異なり、Java でこれを宣言する場合

Foo foo;

インスタンス化された Foo オブジェクトはありません。 foo何も指していない単なるポインタです。

これにより、ヒープに Foo オブジェクトのインスタンスが作成されます。

Foo foo = new Foo();

2 つのスレッドがこのコードを実行している場合、スレッド 1 はFooスタックに参照を持ち、Fooヒープに新しいオブジェクトを割り当てるように要求し、そのFooobj のアドレスを参照に割り当てますfoo。スレッド 2 も同じことをしています。スレッド 2 も新しいFooオブジェクトの割り当てを要求していることに注意してください。これは、スレッド 1 が割り当てられたものとは異なるオブジェクトになります。

これが基本的な (そしてかなり単純化された) 考え方です。

于 2012-07-27T07:32:25.997 に答える
2

両方のスレッドがオブジェクトへの参照を持っている場合、両方のスレッドが同じオブジェクトにアクセスできます。次のような方法がある場合:

public String concat(String a, String b) {
    StringBuilder builder = new StringBuilder();
    builder.append(a);
    builder.append(b);
    return builder.toString();
}

StringBuilder オブジェクトは確かにヒープにありますが、このオブジェクトへの参照を持つスレッドは 1 つだけです。他のスレッドがこの StringBuilder を参照することはできません。したがって、本質的にスレッドセーフです。

それどころか、次の場合:

public String concat(String a, String b) {
    final StringBuilder builder = new StringBuilder();
    new Thread(new Runnable() {
        @Override
        public void run() {
            builder.append("haha!");
        }
    }).start();
    builder.append(a);
    builder.append(b);
    return builder.toString();
}

次に、ローカルで作成されたオブジェクト参照を別のスレッドと共有し、StringBuilder がスレッド セーフではないため、スレッド セーフの問題が発生します。

于 2012-07-27T07:05:15.723 に答える
1

But what if we have a method with non primitive method local variables ? Then if the object for that variable is stored inside the heap, both the threads will have the access to the same object, won't they ? So if that's the case there would be Sync problems

あなた自身の質問に部分的に答えました。その参照値はスタックに保存されますが、実際のオブジェクトの内容はヒープに保存され、 new Object() を呼び出すと、各スレッドはヒープに保存される異なる新しいオブジェクトを作成し、各スレッドはにアクセスします自身のスタックに格納された参照値を使用して作成したオブジェクト

于 2012-07-27T08:59:08.400 に答える
0

何が混乱のポイントになるかについて、私の考えを投げかけます。ヒープはスタックのように管理されていません。はい、すべてのスレッドによって作成されたオブジェクトがヒープにあるという点で共有されます。ただし、各オブジェクトが作成されると、ヒープ内の一意の場所/スペースが与えられます。2 つのスレッドで同時に実行され、オブジェクト インスタンスを作成する 2 つのメソッドは、共有ヒープ内に明確に異なるオブジェクトを作成します。

これらはこの共有ヒープに作成されるため、メソッドfooがオブジェクト参照を返すか、格納するか、間接的に格納する別のメソッドを呼び出すと、foo返されてスタックがポップされても破棄されません。

ガベージ コレクターを持つことの魔法は、この「もの」を追跡する必要がなく、将来の適切な時点で自分で破棄する必要がないことです。コードをシンプルに保ち、アルゴリズム (またはプログラミングの学習) に集中できるようにします。しかし、私は脱線します...

于 2012-07-27T07:38:19.610 に答える
0

ローカル変数は、プリミティブ、別の場所で作成されたオブジェクトへの参照 (割り当てを行う場合)、または新しく作成されたオブジェクトへの参照 (「new」演算子を使用) のいずれかです。

最初のケースでは、あなたが言ったように、問題はありません。

最後のケースでは、新しいオブジェクトをローカルで作成しているため、呼び出しごとに新しいオブジェクトが作成されるため、呼び出しごとにヒープに 1 つのオブジェクトが存在するため、同時実行性の問題はありません。

しかし、2 番目のケースでは、オブジェクトが別の場所で作成されているため、並行性について考える必要があります。

于 2012-07-27T07:04:03.917 に答える