並行プログラミング (マルチスレッド、JMM、可視性、順序付け、原子性、揮発性キーワード、ロック、JUC)

スレッドの基本的な内容を確認する

プログラム: 特定の機能を実現するために特定の言語で記述され、ハードディスクにインストールされる静的プログラム

プロセス: 実行中のプログラム。オペレーティング システムがメモリ領域を割り当てる単位です。

スレッド: スレッドはプロセス内の最小の実行単位であり、CPU スケジューリング単位であり、プロセスに依存します。

スレッドを作成する

1. Thread クラスを継承し、run() をオーバーライドします。

2. クラスは Runable インターフェイスを実装し、run() をオーバーライドします。

Threadクラスのオブジェクトを作成し、それにタスクを割り当てる

3. このクラスは Callable インターフェイスを実装し、call() をオーバーライドして戻り値を持ち、例外をスローできます。

Threadクラスのオブジェクトを作成し、それにタスクを割り当てる

一般的な方法

run() call() start()

名前を設定 優先度を設定

スレッドステータス start() 準備完了ステータス

join() sleep() ブロック状態

yield() スレッドは実行状態に屈し、積極的に放棄します ----> 準備完了状態
stop() スレッドを強制的に中断します (このメソッドは無効で期限切れのメソッドです)
中断() スレッドを中断します

ここに画像の説明を挿入します

マルチスレッド化

Java は、マルチスレッド開発をサポートする最初の言語の 1 つです。

Java は最初からマルチスレッドをサポートしています

能力。現在の CPU のほとんどはマルチコア プロセッサであるため、複数のスレッドを同時に実行できます。

マルチスレッドの利点

マルチスレッド技術によりプログラムの応答性が向上し、他の作業をしながらアクティブに動作させることができます。

ステータス、プログラムのパフォーマンスが向上します。

性能向上の本質はハードウェアの残存価値を引き出すこと(ハードウェア利用率)です。

マルチスレッドの問題

マルチスレッドによって引き起こされる問題にはどのようなものがありますか?

セキュリティ (共有変数へのアクセス)、パフォーマンス (CPU スイッチングのオーバーヘッドなど)

Java同時プログラミング

同時実行性と並列処理

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-o0nJZR6x-1642328944308) (C:\Users\Cloud\AppData\Roaming\Typora\) typora-user-images\ 1642326319274.png)]

Java メモリ モデル (JMM)

JMM は Java メモリ モデルであり、JVM モデルではないことに注意してください。

Java マルチスレッドが動作しているときは、まずメイン メモリ内のデータをスレッドの作業メモリ (キャッシュ) に読み取り、次に作業メモリ内のデータを操作し、操作の完了後にデータをメイン メモリに書き戻します。 。


可視性の問題が発生していますか?

スレッドAで操作したデータはスレッドBでは見ることができません。
ここに画像の説明を挿入します

オペレーティング システムは、命令の実行順序を再配置する場合があります。

注文問題の原因となる
ここに画像の説明を挿入します

スレッドの切り替えにはアトミック性(非分割) の問題が伴います

i=1; i=1;

i++ -->i=i+1 i++ 2

2

++ は計算と代入に分かれていますが、++ 演算はアトミックな演算ではなく、CPU が 2 段階で実行する場合があります。
ここに画像の説明を挿入します

同時プログラミングの中核的な問題

同時プログラミングの中核となる問題 - 可視性、原子性、秩序性

スレッドのローカル キャッシュにより可視性の問題が発生する

コンパイルの最適化と命令の再配置により順序付けの問題が発生する

スレッド切り替えの実行によりアトミック性の問題が発生する

問題を解決するにはどうすればよいですか?

不安定なキーワード

volatile によって変更される共有変数: 1. あるスレッドでの操作後、別のスレッドですぐに参照できることが保証される 2. 最適化命令の並べ替えは禁止される 3. 変数操作のアトミック性は保証できない

i++ はアトミック操作ではありません。ロックの問題を解決するにはどうすればよいですか?

原子性を確保する方法

「同時に 1 つのスレッドだけが実行される」ことを相互排他と呼びます。共有変数への変更が相互に排他的であることを保証できれば、アトミック性を保証できます。

ロック

ロックリエントラントロック

同期化により、可視性と順序付けも保証できます

アトミッククラス

java.util.concurrent包

AtomicInteger は、volatile+CAS を通じてアトミック操作を実装します。

getAndIncrement(); は i++ の代わりに安全です

CAS はスピン思考として理解できます

CAS (Compare-And-Swap) の比較と交換は、同時実行性が低い状況に適しています。

CASは楽観的ロックの実装方法であり、スピンの考え方を採用した軽量なロック機構です。

楽観的ロックとは、ロックせずに解決できる (つまり、ロックが存在しない) 方法を指します。

スピンロック (ポーリング) はループ内でチェックを続けます

CAS には 3 つのオペランドが含まれます。

①メモリ値V

②推定値A(比較時にメモリから再度読み出した値)

③更新値B(更新値)

期待値 A==V の場合に限り、メモリ値 V=B を設定します。それ以外の場合は何も行いません。

この方法はロックよりも効率的であり、判定が失敗して値を更新できない場合でも、ブロックせずに CPU を取得し続けます。

実行する権利、判断し実行し続ける権利

このアプローチではロックがないため、スレッドのブロックが発生しません。

欠点:

同時実行性が高い状況では、スピン メソッドを使用して継続的にループが行われるため、CPU 使用率が高くなります。

ABA の問題が発生する可能性があるため、クラスにバージョン番号を追加して、値が変更されたかどうかを区別できます。

ABAに関する質問

ABA 問題、つまり、スレッドがメモリ値を A から B に変更し、次に B から A に変更します。別のとき

スレッドが期待値を使用して判断すると、期待値はメモリ値と同じになり、変数が変更されていないと誤って認識されます。

問題を引き起こした。

ABA の問題を解決する主な方法は、バージョン番号を追加するなどの方法を使用して ABA の問題を回避することです。

たとえば、元のメモリ値は (A, 1) で、スレッドは (A, 1) を (B, 2) に変更し、その後 (B, 2) に変更します。

(A, 3) に変更します。この時点で、別のスレッドは期待値 (A, 1) とメモリ値 (A, 3) を使用します。

比較の場合、バージョン番号 1 と 3 を比較するだけで、メモリ内のデータが更新されたことがわかります。

JUCクラス

hashMap はスレッドセーフではないため、同時に操作することはできません

HashTable はスレッドセーフであり、変更が同期されています。ロックを put() に直接追加するのは非効率です。これはハッシュ テーブル全体をロックするのと同じです。同時実行性が低い状況でも使用できます。排他的ロック

同時ハッシュマップ

ConcurrentHashMapはスレッドセーフであり、ロックセグメンテーション機構を採用しており、ハッシュテーブル全体をロックしないが、jdk8以降はセグメントロックを使用せず(位置ごとにロックフラグオブジェクトを作成)、CASの考え方+同期型で実装されている

挿入する際には、ハッシュテーブルに対応する位置が先頭ノードであるかどうかを確認し、先頭ノードであればCAS機構(ループ検出)を利用して先頭位置にデータを挿入します。

この位置にすでに値がある場合は、同期された実装を使用して、最初の Node オブジェクトがロックするロック フラグとして使用されます。

public class HashMapDemo {
    
    

    /*
       HashMap是线程不安全的,不能并发操作的
       ConcurrentModificationException  并发修改异常   遍历集合,并删除集合中的数据

       Hashtable 是线程安全的 public synchronized V put(K key, V value)-->独占锁
            锁直接加到了put方法上,锁粒度比较大,效率比较低
            用在低并发情况下可以

       Map<String,Integer> map = Collections.synchronizedMap(new HashMap<>());
       ConcurrentHashMap
     */
    public static void main(String[] args) {
    
    

        ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();
        //模拟多个线程对其操作
        for (int i = 0; i < 20; i++) {
    
    
                 new Thread(
                     ()->{
    
    
                       map.put(Thread.currentThread().getName(), new Random().nextInt());
                         System.out.println(map);
                     }
                 ).start();
        }

    }
}

おすすめ

転載: blog.csdn.net/crraxx/article/details/122526635