第3章:共有オブジェクト
可視
またsynchronize
、メモリの可視性:、同期はまた別の重要な微妙な側面を持っています。我々は、スレッドの修正に別のスレッドによって使用されているオブジェクトの状態を避けることができるようにしたいが、スレッドがオブジェクトの状態を変更する際に、他のスレッドが本当に変化を見ることができていることを確認したかったんではないだけ。
あなたが最初の変数に値を書き込む場合は、シングルスレッド環境では、その後、書面による介入が存在しない場合に、この変数を読んで、あなたは同じ戻り値を取得することができます。読み取りと書き込みが別のスレッドで発生した場合でも、状況は単純にそうではありません。
/*
读线程启动后,主线程虽然把ready赋值为true,
但是由于线程之间存在缓存问题,读线程的reday仍然是false,未能及时获取最新的reday值。
导致程序的运行结果与预期不符。
*/
public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
@Override
public void run() {
while (!ready) {
Thread.yield();
}
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
ロックと可視性
シンクブロックスレッドAを実行するとき、スレッドBはまた、シンクブロックに入り、同じロックモニタである、場合は、変数Aの値がロックBを取得し、表示される前にロックが解除された後に同じが表示されていることを保証することができます。
ロックは、同期や排他制御についてだけではなく、また、メモリについて見えます。すべてのスレッドが共有最新の値を参照できるようにするために、同じ共有変数の読み書きのスレッドを含む可変変数は、共通のロックを使用して同期する必要があります。
揮発性変数
:Java言語は、他の選択肢、すなわち弱い同期の形提供していますvolatile
変数を。
それは、それが他のスレッドを通知する予測可能な方法で変数を更新し保証します。
フィールドは以下のように宣言されたときにvolatile
型、モニタコンパイラおよびランタイム変数:それが共有され、その動作は並べ替えされず、他のメモリ操作から。
だから、読んでvolatile
変数の時の型は、常にいくつかのスレッドによって書かれた最新の値を返します。
アクセスvolatile
操作する変数がロックされていない、それが作る実行の妨害スレッド、発生しませんvolatile
変数の相対的sychronized
用語を、しかし軽量な同期メカニズム。
ロックは、視認性や耐原子を確保することができ、
volatile
変数は、視認性を確保することができます。
以下の基準をすべて満たした後にのみ、あなたが使用することができvolatile
、変数を:
- これは、変数を書くときに、変数の現在の値に依存し、または単一のスレッドのみが変数の値を変更することを保証することはできません。
- 変数は、他の状態変数一定の制約に参加する必要はありません。
- あなたは変数にアクセスする場合や、ロックする他の理由はありません。
注:アトミック変数
java.util.concurrent.atomic
()
閉じられたスレッド
共有変数データの同期化へのアクセスが必要です。同期した方法を避けるためには、一般的なデータではありません。データは、単一のスレッドにアクセスされた場合は、任意の同期を必要としません。
スレッド閉じ(スレッド閉じ込め)技術は、スレッドセーフを達成する最も簡単な方法の一つです。
オブジェクトがスレッドに囲まれている場合は、このアプローチは、自動的にスレッドセーフではありますが、オブジェクト自体がクローズされていない場合でも。
一般的なアプリケーションは、スレッドの制限にJDBC(Javaデータベース接続性)Connectionオブジェクトのアプリケーションプールを使用することです。
JDBC仕様では、Connectionオブジェクトは、スレッドセーフである必要はありません。
各スレッドが返される前に(例えばサーブレット要求やEJB(エンタープライズJavaビーン)の呼び出しなど)だけでなく、接続オブジェクトで同期ほとんどの要求を処理します、プールは、他のスレッドにそれを再配布するので、この接続管理されることはありません暗黙的にリクエストの処理中にスレッドに制限モードのConnectionオブジェクト。
スタックリミット
(また、コアライブラリクラスのない内部スレッドまたはスレッドローカル使用と呼ばれるが、スタック制限ThreadLocal
混乱)はスレッドの制限、スタック上の制限の特別な場合で、これはローカル変数でターゲットに到達することができます。
パッケージには一定の制約が開催されることが容易になりますと、ローカル変数は、ローカルのスレッドでより簡単にオブジェクトを作るために制限されています。
ローカル変数自体は実行スレッドに制限されている、彼らは実行スレッドのスタックに存在しています。他のスレッドがスタックにアクセスすることはできません。
ThreadLocalの
スレッド制限を維持するためのより多くの標準化された方法で使用することですThreadlocal
、それはあなたがホールド値を各スレッドにオブジェクトを関連付けることができます。ThreadLocal
これは、提供各コピーのための別々のスレッドを維持するためにそれを使用して、アクセス装置。だから、常にによって現在の実行スレッドによって返された最新の値セット。get
set
get
set
不変性
オブジェクトを変更することができない状態を作成した後に不変オブジェクトと呼ばれます。
不変オブジェクトは常にスレッドセーフになります。
どちらもJava言語仕様とJavaのメモリモデルが不変の正式な定義ではありませんが、不変のように宣言されているすべてのドメインオブジェクトに単純に等しくないfinal
タイプは、すべてのフィールドがあるfinal
オブジェクト型はまだ可変とすることができますので、final
参照フィールドは、変数オブジェクトを取得します。
以下の条件を満たす、オブジェクトは不変であるのみ:
- その状況は、作成後に変更することはできません。
- すべてのフィールドがあり
final
、かつ、種類- これは、(作成が中にこの参照脱出を実現しなかった)が正しく作成されました。
String
これは、Javaで不変オブジェクトの典型的な例です。
第4章:オブジェクトのグループ化
デザインスレッドセーフなクラス
プロセス設計スレッドセーフなクラスには、次の3つの基本要素を含める必要があります。
- 変数から構成されているオブジェクトの状態を決定するステップと
- 不変条件は、状態変数の限界を決定します。
- オブジェクトの状態に経営戦略の同時アクセスを開発します。
用語集:
- プリオリ条件(前提条件):方法(メソッド)、真でなければならない条件を呼び出す前にメソッドを定義します。たとえば、あなたが空のキューから一つのエントリを削除することはできません:あなたは要素を削除する前に、キューは「非空」状態である必要があります。
- 実験条件後(事後条件):成功した実装が完了しなければならない後の工程の真の条件を定義する、方法に関する。例えば、+1インクリメントカウンタ17の現在の状態は、次の状態18のみ正当である場合。
制約の例
オブジェクトはスレッドセーフでない場合でも、まだそこにある多くの技術は、マルチスレッドプログラムのためにそれを安全にすることができます。
たとえば、あなたは確かにそれがシングルスレッドアクセス(スレッド制限)であることを、あなたはまた、すべてのアクセスが正しくロックによって保護されることを保証することができますすることができます。
データをオブジェクト内にカプセル化される、方法でデータオブジェクトへのアクセス制限は、スレッドが正しいロック・アクセス・データを確保するために常に容易です。
/**
* 使用限制确保线程安全
*/
@ThreadSafe
public class PersonSet {
@GuardedBy("this")
private final Set<Person> mySet = new HashSet<Person>();
public synchronized void addPerson(Person p) {
mySet.add(p);
}
public synchronized boolean containsPerson(Person p) {
return mySet.contains(p);
}
}
いくつかのクラスを含むライブラリプラットフォーム制限の例は、その存在はスレッドセーフに非スレッドセーフなクラスを持っている、多くのスレッドにあります。
ArrayList
そしてHashMap
、このような基本的な非スレッドセーフなコンテナに、しかし、ラッパークラスライブラリ植物(の方法を提供Collections.synchronizedList
し、同じファミリーの方法を)、非スレッドセーフなクラスが安全に、マルチスレッド環境で使用することができるようになっています。
既存のスレッドセーフなクラスに機能を追加
「に追加された不足(PUT-IF-不在は)」という概念は、それがすでに存在する場合、コンテナに要素を追加する前に11最初のチェックは非常に簡単で、存在する場合には、追加しないでください。
(「チェックの実行(チェックイン後、-ACT)」今、アラームを鳴らすこと)。
クラスのスレッドの安全要件は、暗黙的に、そのような操作はアトミックでなければならない「つまり追加欠落している」などの別の11点の条件を追加しました。
任意の合理的な要求は、あなたがXが含まれていないListオブジェクトを持っている場合は、あなたを教え、そしてADDXは、コンテナの結果はXののコピーをのみ含まれている必要があります「とは、不足している追加」、2回実施されますがあります。
しかし、アトミック操作はコンテナはXの2つのコピーを含有する原因いくつかの散発的なタイミングで、2つのスレッドが、Xを見て、ADDXを実行するための容器に含まれていない、ではない「すなわち添加欠けている」場合
-
方法は、所望の動作をサポートするために、元のクラスを変更することで最も安全な新しいアトミック操作を追加します。
しかし、あなたはアクセスまたはソースコードを変更するには無い自由ではないかもしれないので、それは通常は不可能です。
あなたは元のクラスを変更することができたとしても、同期化戦略は、オリジナルのデザインを維持する前提の下で、その機能を向上させるためには、その実装を理解する必要があります。
新しい方法はとても簡単で理解し、維持するために、クラスに直接、すべてのコードの実装クラスの同期化政策はまだファイルのソースコードが含まれていることを意味して添加します。 -
もう一つの方法は、クラスを拡張できるように設計されている場合、クラスを拡張することです。
4.13をリストBetterVector
拡張Vector
および追加putIfAbsent
方法を。拡大がVector
実際にはかなり直感的ではなく、すべてのクラスは、このプログラムをサポートするためのサブクラスが公開に状態の十分な数を与えました。
@ThreadSafe
public class BetterVector<E> extends Vector<E> {
public synchronized boolean putIfAbsent(E x) {
boolean absent = !contains(x);
if (absent) {
add(x);
}
return absent;
}
}