2013-05-23 12 views
5

のは、私はこのような別のスレッドから更新されますJavaBeanのユーザーあるとしましょう:スレッドセーフ

public class A { 

    private final User user; 

    public A(User user) { 
     this.user = user; 
    } 

    public void aMethod() { 
     Thread thread = new Thread(new Runnable() { 

      @Override 
      public void run() { 
       ...a long running task.. 
       user.setSomething(something); 
      } 

     }); 
     t.start(); 
     t.join(); 
    } 

    public void anotherMethod() { 
     GUIHandler.showOnGuiSomehow(user); 
    } 
} 

が安全にこのコードのスレッドですが? A.インスタンスを作成し、A.aMethodと呼ばれるスレッドがユーザーフィールドを読み込むと、新しい状態のユーザーが表示されますか?どのように適切なスレッドセーフな方法でそれを行うには?

私はユーザークラスを変更できません。スレッドセーフであるかどうかはわかりません。

答えて

4

このコードはスレッドセーフですか? ...それは新鮮な状態でユーザーを見ますか?

特に、コード内でuserが最後であるという事実は、置き換えられないという事実以外はスレッドの安全性にほとんど違いがありません。

変更するビットは、setSomethingで設定されるインスタンス変数です。それはvolatileとマークする必要があります。

class User { 
    // Marked `volatile` to ensure all writes are visible to other threads. 
    volatile String something; 

    public void setSomething(String something) { 
    this.something = something; 
    } 

} 

しかし(あなたがお勧めのように)あなたがUserクラスへのアクセス権を持っていない場合、あなたはその後、メモリバリアを作成し、同期を実行する必要がありますが。最も単純な形式では、userへのアクセスを囲むことができ、​​アクセス権があります。

synchronized (user) { 
    user.setSomething(something); 
} 

を追加しました: - それは、これが実際にこのように行うことができること(hereを参照)判明:

volatile int barrier = 0; 
... 
user.setSomething(something); 
// Forces **all** cached variable to be flushed. 
barrier += 1; 
+0

Userクラスを変更することはできません。 – Behnil

+0

@Behnil - 回答が拡張されました。 – OldCurmudgeon

4

フィールドをマーキングfinalとしてだけ参照を変更することができないことを意味します。クラスUserのスレッドセーフティについては何も意味しません。フィールドにアクセスするこのクラスのメソッドが同期されている場合(または他の同期手法を使用する場合)、スレッドセーフです。それ以外の場合はそうではありません。

+0

アップデートの質問をご覧ください。 – Behnil

0

finalは、参照が再割り当てできないようにしますが、参照が変更可能なクラスを指している場合は、そのオブジェクト内の状態を変更することができます。

あなたのコードは、Userクラスが不変の場合にのみスレッドセーフです。 Userのすべてのプロパティをオブジェクトの外で変更することはできません。クラス内のすべての参照は、他の変更不可能なクラスを指します。

大文字小文字の区別がない場合は、そのメソッドを適切に同期させてスレッドセーフにする必要があります。

0

ユーザークラスは変更できません。スレッドセーフであるかどうかはわかりません。

ユーザーオブジェクトにアクセスする際に、アクセスを同期させる必要があります。ユーザーオブジェクトにのみ非同期あなたのスレッドにアクセスされることを前提としてい

synchronized(user) { 
    // access some method of the user object 
} 

: あなたは、たとえば、これだけのようなもので、ユーザーオブジェクト上のすべてのアクセスをラップ、同期するためにユーザオブジェクトを使用することができます。また、同期ブロックを短くしてください。

また、ユーザーオブジェクトの周りにスレッドセーフラッパーを構築することもできます。私は、あなたがたくさんの異なる呼び出しを持っているならば、コードがより洗練され、読みやすくすることを提案します。

幸運!スレッドに関する

0

finalフィールドは、単にメモリバリアメカニズムについてJSR-133以来、constructor escapeの場合には一貫性が保証されていますオブジェクトの最終フィールドの

値は、コンストラクタで設定されています。 オブジェクトが "正しく"構築されたと仮定すると、オブジェクトが であると、 同期なしで、 コンストラクタの最後のフィールドに割り当てられた値が他のすべてのスレッドから見えます。さらに、他のオブジェクト またはその最終フィールドによって参照される配列の可視値は、少なくとも最終フィールドとして と最新のものになります。オブジェクトが正しく構成されているとはどういう意味ですか。 これは単に、建設中にオブジェクト への参照が「エスケープ」することを許可されていないことを意味します。 ( の例を参照してください)換言すれば、 は、 他のスレッドがそれを見ることができる場所であれば、構築されているオブジェクトへの参照を配置しないでください。静的な フィールドには割り当てないでください。他のオブジェクトにリスナーとして登録しないでください。 がオンになります。これらのタスクは、コンストラクタが完了した後に実行し、コンストラクタを に実行する必要はありません。

しかし、は、残りのオブジェクトの寿命(クラスのコンストラクタの実行をラップした後の意味)の最終フィールドについて自動スレッドセーフを保証しません。。実際、Javaの不変性は、純粋な誤称です。

ここでは、不変性とは「変更しない」という意味です。 不変性は、Javaでは「変更されません」という意味ではありません。つまり、最後のフィールドから が推移しており、最後のフィールドが であるため変更されていません。最終フィールドが のオブジェクトへの参照はコンストラクタからエスケープされませんでした。

+0

私は、不変性の定義がかなり正しいとは思わない。オブジェクトへの参照が 'final'フィールドに格納されていて、そのオブジェクトへの他の参照も存在する場合、私はそのオブジェクトが参照されていない* 'final'フィールド*から読み込みまたはコピーします。 – supercat

0

はい、これは安全です。

Java Language Specification (Java 8) Chapter 17.4.4を参照:

スレッドT1の最後のアクションが同期-とT1が終了したことを検出する別のスレッドT2で任意のアクション。

T2は、T1.isAlive()またはT1.join()を呼び出すことでこれを実現できます。17.4.5. Happens-before Orderと一緒にこれを入れて

つのアクションが事前発生関係で注文することができます。 1つのアクションが発生すると、別のアクションの前に、最初のアクションが表示され、2番目のアクションの前に順序付けられます。 [..]アクションxが次のアクションyと同期する場合、hb(x、y)も持ちます。

コードでt.join();を呼び出した後、更新された変更が表示されます。 「Aインスタンスを作成し、A.aメソッドと呼ばれるスレッドはaMethodを呼び出した後、メソッドでビジー状態であるためt.joinが呼び出される前に値を読み込むことができないため、これは安全です。