コードは、複数のスレッドによって同時に呼び出された場合でも安全です。これをスレッド セーフと呼びます。コードがスレッドセーフであれば、競合状態は含まれません。競合状態は、複数のスレッドが共有リソースを更新する場合にのみ発生します。したがって、Java スレッドがいつ共有リソースを実行するかを知ることが重要です。
ローカル変数
ローカル変数は、各スレッドの独自のスタックに保存されます。つまり、ローカル変数はスレッド間で共有されません。これは、すべてのローカル プリミティブ変数がスレッドセーフであることも意味します。以下に例を示します。
public void someMethod(){ long threadSafeInt = 0; threadSafeInt++; }
ローカル オブジェクト参照
オブジェクトへのローカル参照は少し異なります。参照自体は共有されません。ただし、このオブジェクトへの参照をすべてのスレッドのスタックに保存することはできません。すべてのオブジェクトは共有ヒープに格納されます。
ローカルに作成されたオブジェクトは、作成されたメソッドをエスケープしない限り、スレッドセーフです。実際、渡されたオブジェクトのメソッドが他のスレッドで使用できない限り、それを他のメソッドに渡すこともできます。
例を次に示します。
public void someMethod(){ LocalObject localObject = new LocalObject(); localObject.callMethod(); method2(localObject); } public void method2(LocalObject localObject){ localObject.setValue("value"); }
この例の LocalObject インスタンスは、このメソッドから返すことも、他のオブジェクトに渡すこともできません。これは someMethod メソッドの外部からアクセスできます。 someMethod メソッドを実行する各スレッドは、独自の LocalObject インスタンスを作成し、それを localObject 参照に割り当てます。したがって、この使用法はスレッドセーフです。
実際、この someMethod メソッド全体はスレッドセーフです。この localObject インスタンスが同じクラスの他のメソッドまたは他のクラスにパラメータとして渡された場合でも、スレッドセーフに使用できます。
もちろん、唯一の例外は、引数として LocalObject を使用してこれらのメソッドの 1 つが呼び出され、何らかの方法で LocalObject インスタンスを保存し、他のスレッドからのアクセスを許可する場合です。
オブジェクトのメンバー変数
オブジェクトのメンバー変数(フィールド)は、オブジェクトとともにヒープ上に格納されます。したがって、2 つのスレッドが同じオブジェクト インスタンスのメソッドを呼び出し、このメソッドがオブジェクトのメンバー変数を更新する場合、メソッド全体はスレッドセーフではありません。以下に例を示します。
public class NotThreadSafe{ StringBuilder builder = new StringBuilder(); public add(String text){ this.builder.append(text); } }
2 つのスレッドが同じ NotThreadSafe インスタンス上で add メソッドを同時に呼び出すと、競合状態が発生します。例:
NotThreadSafe sharedInstance = new NotThreadSafe(); new Thread(new MyRunnable(sharedInstance)).start(); new Thread(new MyRunnable(sharedInstance)).start(); public class MyRunnable implements Runnable{ NotThreadSafe instance = null; public MyRunnable(NotThreadSafe instance){ this.instance = instance; } public void run(){ this.instance.add("some text"); } }
2 つの MyRunnable インスタンスが同じ NotThreadSafe インスタンスを共有していることに注目してください。したがって、add メソッドを呼び出すときに競合状態が発生します。
ただし、2 つのスレッドが異なるインスタンスで同時に add メソッドを呼び出した場合、競合状態は発生しません。以下は、前の例を少し変更したものです:
new Thread(new MyRunnable(new NotThreadSafe())).start(); new Thread(new MyRunnable(new NotThreadSafe())).start();
これで、各スレッドは独自の NotThreadSafe インスタンスを持ち、add メソッドの呼び出しが互いに干渉しなくなりました。このコードには競合状態はありません。したがって、オブジェクトがスレッドセーフでない場合でも、競合状態を引き起こすことなくこの方法で使用できます。
スレッド制御オーバーフロールール
コードがリソースにアクセスする際にスレッドセーフであるかどうかを判断する場合、次のルールを使用できます:
If a resource is created, used and disposed within the control of the same thread, and never escapes the control of this thread, the use of that resource is thread safe.
リソースは任意の共有リソースにすることができます。オブジェクト、配列、ファイル、データベース接続、ソケットなど。 Java では、常にオブジェクトを明示的に破棄できるわけではないため、「破棄された」ということは、オブジェクトが欠落しているか、null 参照があることを意味します。
オブジェクトの使用がスレッドセーフであっても、そのオブジェクトがファイルやデータベースなどの共有リソースを指している場合、アプリケーション全体がスレッドセーフではない可能性があります。たとえば、スレッド 1 とスレッド 2 がそれぞれ独自のデータベース接続を作成する場合、それぞれ独自の接続を使用する接続 1 と接続 2 はスレッドセーフになります。ただし、この接続が指すデータベースの使用はスレッドセーフではない可能性があります。たとえば、2 つのスレッドが次のようなコードを実行した場合:
check if record X exists if not, insert record X
2 つのスレッドがこれを同時に実行すると、最終的にレコードが挿入されて終了します。以下に示すように:
Thread 1 checks if record X exists. Result = no Thread 2 checks if record X exists. Result = no Thread 1 inserts record X Thread 2 inserts record X
これは、ファイルまたは他の共有リソースを操作するスレッドでも発生します。したがって、スレッドによって制御されるオブジェクトがリソースであるか、それともそのリソースへの単なる参照 (データベース接続など) であるかを区別することが重要です。
上記は Java スレッドと共有リソースの内容です。その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。