2009-08-23 10 views
3

私はThreadLocalを使用して、既存のスレッドセーフでないクラスにスレッドセーフを提供しようとしていますが、問題が発生しています。それは、分離が実行されていないように見えます - スレッドは各スレッドにローカルではなく、静的を共有します。スタティックメンバーを含む既存のクラスでThreadLocalを使用する

this StackOverflow questionに記載されているSimpleDateFormatterのローカリゼーションの例とほぼ同じですが、希望通りに動作しません。

私が望んでいるのは、これを使用している誰かが、私が作っているはずの間違いのない間違いを指摘してくれるということです。私の質問は、私がここで間違っていることに気づくことができますか?

は、ここに私の単純なクラスです:

public class SimpleClassWithStaticMembers { 
    private static String theStaticString = 
     "StaticStringInClassWithStaticMember"; 
    public void setTheStaticString (String val) { 
     SimpleClassWithStaticMembers.theStaticString = val; 
    } 
    public String getTheStaticString() { 
     return SimpleClassWithStaticMembers.theStaticString; 
    } 
} 

そして、これはSimpleClassWithStaticMembersのThreadLocalのインスタンスを作成し、スレッドクラスです:

public class SimpleTesterThread extends Thread { 
    private void showMsg (String msg) { 
     System.out.println (msg); 
     System.out.flush(); 
    } 
    public SimpleTesterThread (String threadId) { 
     super(threadId); 
    } 
    public void run() { 
     try { Thread.sleep(2000); } catch (InterruptedException ex) { } 
     ThreadLocal<SimpleClassWithStaticMembers> localizedClass = 
      new ThreadLocal<SimpleClassWithStaticMembers>(); 
     localizedClass.set(new SimpleClassWithStaticMembers()); 
     // repeating here to be sure we overlap all with all 
     for (int ii=0; ii < 3; ii++) { 
      localizedClass.get().setTheStaticString ("Setby_" + this.getName()); 
      try { Thread.sleep(2000); } catch (InterruptedException ex) { } 
      showMsg("   Thread [" + this.getName() + "] - " 
       + localizedClass.get().getTheStaticString() + "'."); 
     } 
     showMsg ("Thread [" + this.getName() 
      + "] complete. Our 'threadlocal' string is now - " 
      + localizedClass.get().getTheStaticString() + "'."); 
     localizedClass.remove(); 
    } 
} 

SimpleTesterThreadの10件のインスタンスが作成されると(彼らにのような個別のスレッド名を与えます"AAAAAAAAAA"、 "BBBBBBBBB"など)、開始されると、出力はそれらがインスタンスを共有していることが明確に示されます。出力が含まれてログインします。

 
... 
      Thread [JJJJJJJJJJ] - Setby_CCCCCCCCCC'. 
      Thread [DDDDDDDDDD] - Setby_JJJJJJJJJJ'. 
      Thread [IIIIIIIIII] - Setby_DDDDDDDDDD'. 
      Thread [GGGGGGGGGG] - Setby_IIIIIIIIII'. 
      Thread [EEEEEEEEEE] - Setby_GGGGGGGGGG'. 
Thread [EEEEEEEEEE] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
      Thread [HHHHHHHHHH] - Setby_GGGGGGGGGG'. 
      Thread [BBBBBBBBBB] - Setby_GGGGGGGGGG'. 
      Thread [FFFFFFFFFF] - Setby_GGGGGGGGGG'. 
Thread [FFFFFFFFFF] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
... 
      Thread [JJJJJJJJJJ] - Setby_GGGGGGGGGG'. 
Thread [JJJJJJJJJJ] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
Thread [HHHHHHHHHH] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
      Thread [GGGGGGGGGG] - Setby_GGGGGGGGGG'. 
Thread [GGGGGGGGGG] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
      Thread [IIIIIIIIII] - Setby_GGGGGGGGGG'. 
      Thread [CCCCCCCCCC] - Setby_GGGGGGGGGG'. 
Thread [CCCCCCCCCC] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
Thread [AAAAAAAAAA] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
      Thread [DDDDDDDDDD] - Setby_GGGGGGGGGG'. 
Thread [DDDDDDDDDD] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
Thread [IIIIIIIIII] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
============== all threads complete. 

私が作成したクラスが含まれていない、開始し、スレッドに参加する - それが有用であることが感じたなら、私は喜んでそれを追加して編集します。

+0

問題は、あなたが 'start()'メソッドの代わりに 'run()'メソッドを呼び出すことによってスレッドを "開始"しているのだろうか... ... –

+0

提案のおかげで、スティーブン。スレッドはstart()メソッドで開始されていますが、それは確かに効果があります。 – CPerkins

答えて

5

すべてのインスタンスが同じ静的フィールドと同じように、別々のインスタンスを持つことは役に立ちません。変更可能な統計は悪です。

本当にクラスを変更できない場合は、ロックを使用して、各クライアントが一度に1つずつ静的フィールドを使用できるようにすることができます。静的フィールドのインスタンスを変えたい場合は、クラスローダーを使って遊ぶ必要があります(別の明白な解決策はバイトコードを書き換えることです)。

+0

私はthreadlocal以外の静的については知っていますが、ここや他の場所でthreadlocalが静的なスレッド固有のコピーを与えていると思いました。クラスローダごとにではなくスレッド単位に変換しています。そうではありませんか? – CPerkins

+0

'ThreadLocal'は、各スレッドに異なる参照を持つ能力を与えます。それは何も魔法をしません。異なるスレッドで同じ参照を設定し、まったく同じオブジェクトで終わることさえできます。 –

+0

これは残念です。しかし、ありがとう。 – CPerkins

2

ThreadLocalクラスは、静的フィールド修飾子の動作を変更しません。静的フィールドは、クラスが初期化されるときに作成され、初期化されるため、クラスの複数のインスタンスにわたって1つのインカネーションを引き続き持ちます。

これをより詳しく説明すると、ThreadLocalメンバーは、スレッドごとに存在するMapを使用して内部的に管理されます。 get()とset()の呼び出しは、このマップ上で動作します。 ThreadLocalには「魔法」はなく、静的メンバーは単一の化身を持つという性質を失います。静的メンバーがThreadLocal変数として設定されている場合は、そのスレッドによって実行されるコードの別のセクションで参照用に単純にマップに追加されます。これは、2番目のスレッドが静的メンバーへの参照を取得し、メンバー・フィールドに対して操作を実行することを妨げません。

これは、トム・ホーティンの言葉を真剣に受け止めなければならないという理由によるものです。可変性の統計は単に良いデザインではありません。

PS:ThreadLocalオブジェクトの動作に関する誤解を解決するには、Thread、ThreadLocal、およびThreadLocal.ThreadLocalMapクラスの実装を見てください。

+0

ありがとう、私はそれをやるでしょう。 – CPerkins

関連する問題