最初の同期とは何ですか?
同期とは、マルチスレッド環境でオブジェクトの整合性を確保することです。同時に、スレッドが同期されたコードブロックまたはメソッドに入ると、同じロックで保護されている前のスレッドのすべての変更効果を確認できます。
Java言語は、longおよびdoubleデータの読み取りと書き込みを除いて、変数の読み取りまたは書き込みがアトミックであることを保証します(long、doubleデータの読み取りと書き込み、AtomicLong / Doubleを使用したlong / doubleの読み取りと書き込みの場合、非アトミックなのはなぜですか?アトミックを保証メモリの可視性を確保しながら)、変数の読み取りまたは書き込み時に操作を同期する必要はなく、操作のアトミック性を確保できますが、JMM(Javaメモリモデル)の制限により、次のようになります。図JMMの概略図:図は次の
とおりです。https://juejin.im/post/5d5df4d7518825661a3c1dfe
各スレッドが動作しているとき、メインメモリから変数をコピーして独自の動作メモリに格納するため、スレッドは変数Changesが別のスレッドに表示される保証はありません。したがって、読み取りと書き込みの両方がアトミックである場合でも、マルチスレッド環境でメモリを表示するために同期する必要があります。
- 同期せずにマルチスレッドで共有変数にアクセスする例:
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(() -> {
int i = 0;
while (!stopRequested) {
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
上記のコードは、同期がなく、あるスレッドによる変数の変更が別のスレッドによって取得されないため、実行後に停止しません。
- 共有変数へのアクセスを同期するために同期を使用する例
public class StopThread {
private static boolean stopRequested;
private static synchronized void requestStop(){
stopRequested=true;
}
private static synchronized boolean stopRequested(){
return stopRequested;
}
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread=new Thread(() -> {
int i=0;
while (!stopRequested()){
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
requestStop();
}
}
上記のコードは実行後約1秒で停止しました。これは予想どおりですが、同期に同期を使用しています
。両方のメソッドに同期を追加するのはなぜですか。次の2つの同期により、メモリが確実に表示されるためです。ここでは、同期された通信ルールのみを使用します。これは、メソッドJava自体の読み取りおよび書き込み操作により、それがアトミックであることが保証されるためです。
Synchronizedの2つの規制により、メモリの可視性が保証されます。
1.スレッドのロックを解除する前に、共有変数の最新の値をメインメモリに更新する必要があります
。2。スレッドがロックされると、作業メモリ内の共有変数の値がクリアされるため、共有の使用が可能になります。変数はメインメモリから最新の値を再読み込みする必要があります(ロックとロック解除には統合ロックが必要です)
ミューテックスコードのスレッド実行のプロセス:
1。ミューテックスを取得します
2.ワーキングメモリをクリアします
3.メインメモリからワーキングメモリに最新の変数コピーをコピーします
4.コードブロックを実行します
5.変更された共有変数値をフラッシュしますメインへ
6.メモリ内のミューテックスロックを解除します
http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#incorrectlySync
したがって、同期のために1つに同期を
追加し、stopRequested()にのみ同期を追加する場合、requesttStop()が最新の値をメインメモリに書き込むことを保証する方法はありません。メインメモリに書き込まれない場合は、 stopRequested()が最新の値を取得する方法はありません。
同期をrequestStop()に追加するだけの場合、stopRequested()によって取得された値が最新であることを保証する方法はありません。
スレッド間通信の効果のみを実現する必要がある場合は、volatileキーワードを使用してメモリの可視性を確保し、スレッドが変数を読み取るときに変数の最新の値を取得できるようにします。
- マルチスレッド環境での同期と共有変数へのアクセスにvolatileを使用する
public class StopThread {
private static volatile boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(() -> {
int i = 0;
while (!stopRequested) {
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
上記のコードは、約1秒間実行すると自動的に停止し、目的の効果が得られます。
ただし、同期にvolatileを使用する場合、++は2つのステップに分割されるため、volatileで変更された変数の++操作は非アトミックであることに注意する必要があります。最初に古い値を読み取り、次に1を追加します。 2番目のスレッドは最初のスレッドにありますスレッドが古い値の読み取りと新しい値の書き戻しの間に変数を読み取る場合、2つのスレッドは最終的に同じ値を取得します。
では、上記の問題を回避する方法は?
Atomicパッケージのツールを使用します。
例:AtomicLongを使用して、マルチスレッド環境でシリアル番号を生成します。
private static AtomicInteger nextSerialNumber = new AtomicInteger();
public static long generateSerialNumber() {
return nextSerialNumber.getAndIncrement();
}
上記のコードは、ロックせずに一意のシリアル番号を取得することを保証できます。
java.util.concurrent.atomicパッケージの下でAtomicLongクラスを使用します。このクラスは、ロックせずに単一の変数でスレッドセーフなプログラミングを実行できることを保証します。つまり、このクラスは通信効果だけでなく、アトミック性も保証します。操作の数が保証されます(AtomicLong / Doubleを使用してlong / doubleを読み書きすると、アトミック性が確保され、メモリの可視性が確保されます)
つまり、複数のスレッドが変数データを共有する場合は、マルチスレッドの同期を確保することが非常に重要です。
上記は、共有変数データへのJava同期アクセスについての私自身の学習と理解です。エラーがありましたら訂正してください。皆様のご連絡をお待ちしております。