Java スレッドと Java マルチスレッドの詳細な紹介

目次

1. プロセスとスレッドの違い

   1.1 プロセス

   1.2 スレッド

2. 同時実行性と並列性

   2.1 パラレル

   2.2 同時実行性

   2.3 スレッドの実行を監視する

3. 作成方法

3.1 Threadクラスの継承          

考えてみます: オブジェクトを通じて start() メソッドを直接呼び出してみてはどうでしょうか?

3.2 Runnableインターフェイスの実装

3.3 Callable を使用したスレッドの作成

3.4 呼び出し可能と実行可能の違い

Thread の継承と Runnable インターフェイスの実装の違い

3.5 Executorフレームワークなどのスレッドプールを利用する

3.5.1 コンセプト

3.5.2 利点

3.5.3 スレッドプール実行の基本原則

3.5.4 スレッドプールの作成方法

3.5.5 スレッドプールの分類

3.5.6 スレッドプールのコアパラメータ

4. 一般的な方法

5、マルチスレッド実装

6. スレッドの終了

7. 一般的な方法

 8、スレッドのライフサイクル

8.1 スレッドの状態

8.2 Wait()、notify()、notifyAll() の類似点

9、スレッドの安全性

 9.1 セキュリティ問題の理由 編集

 9.2 同期済み

  9.3 ロック

  9.4 ねじロック

  9.5 並行ツールクラス ConcurrentHashMap HashMap HashTable

           9.6 スレッドのデッドロック

           9.7 ロックの解除

10. 考える質問

1. 揮発性キーワード

2. count++ はアトミック操作ですか?

3. アトミックな操作を保証する方法

4. 原子クラスの原則 -- CAS 原則

5. ConcurrentHashMap HashMap と HashTbale の違い



1. プロセスとスレッドの違い


   1.1 プロセス

        オペレーティング システムで実行されているソフトウェア (使用している QQ など) がプロセスを開始し、オペレーティング システムがそのプロセスに新しいメモリ領域を割り当てます。プロセスは独立しており、動的で同時実行性があります。

  1. 独立性: プロセスは独立して実行できる基本単位であり、システムがリソースを割り当ててスケジュールするための独立した単位でもあります。
  2. ダイナミクス: プロセスの本質はプログラムの実行プロセスであり、プロセスは動的に生成され、動的に消滅します。
  3. 同時実行性: どのプロセスも他のプロセスと同時に実行できます。

   1.2 スレッド

        スレッドはプロセスによって作成され、プロセスの実体であり、プロセスは複数のスレッドを持つことができます。プロセス内の単一の連続した制御フロー、つまり実行パスです。それがアプリケーションの役割です。     例: 360 ソフトウェアのウイルス対策、トロイの木馬のスキャン         、
        ガベージ
            の
        クリーニング             通知による         デーモン スレッドの             通常、ワーカー スレッドとして機能します。すべてのユーザー スレッドが終了すると、デーモン スレッドは自動的に終了します。共通デーモン スレッドにはガベージ コレクション メカニズム アプリケーションがあります
            。:                 メインスレッドが終了すると、子スレッドも自動的に終了することを希望する場合は、最後に子スレッドをデーモンスレッドとして設定するだけで済みます。メソッドinstance.setDaemon()をinstance.start()の前に設定する     注: プロセスには少なくとも1つのスレッドが含まれます






2. 同時実行性と並列性

  2.1 パラレル

        同時に、複数の命令が複数の CPU で同時に実行されるため、
        一見同時のような錯覚が生じますが、簡単に言えば、シングルコア CPU によって実現されるマルチタスクは並列処理です。

  2.2 同時実行性

        同時に、複数の命令が複数の CPU で交互に実行されます
        マルチコア CPU は並列実行可能
    注: 同時実行と並列処理が同時に発生する可能性があります

  2.3 スレッドの実行を監視する

      ターミナルに Jconsole と入力して、スレッドの実行を監視します。

3. 作成方法

3.1 Threadクラスの継承



               

         Threadクラスを継承するとスレッドとして利用できるようになります。多くの場合、run() を書き直し (スレッドは Runnable インターフェイスを実装します)、このメソッドに独自のビジネス ロジックを記述し、操作用のこのクラスのインスタンスを作成し、そのインスタンスを通じて start メソッドを呼び出す必要があります。

考えてみます: オブジェクトを通じて start() メソッドを直接呼び出してみてはどうでしょうか?


    プログラムを実行するとき、それはスレッドを開始するのと同じです。プロセスが開始されると、プロセス内でメイン スレッドが開かれます。メイン スレッドが instance.start() を実行すると、メイン スレッドはブロックされず、継続して実行されます。 run() を直接呼び出すと、このときの run() は通常のメソッドなのでブロックされ、実際のスレッドは開かれず、他のステートメントは run() が実行された後にのみ実行されます。マルチスレッドの効果を実際に実現するのは、run() ではなくネイティブ メソッド start0() です。start0() は JVM によって呼び出され、最下層は c/c++ によって実装されることに注意してください (メインスレッドはタスクの実行後に終了しますが、サブスレッドが実行されている場合、プロセスは終了しません。すべてのスレッドが終了すると、終了、プロセスは終了しました)

3.2 Runnableインターフェイスの実装

    注: Java は単一継承であり、クラスが特定の親クラスを継承している場合がありますが、Thread クラスのメソッドを継承してスレッドを作成することは明らかに不可能です。したがって、Java 設計者は、スレッドを作成するための別のメソッドを提供します。これは、メソッドを実装するために Runnable インターフェイスを実装することです。新しいスレッドを作成する必要があり、Runnable インターフェイス メソッドを実装するクラスのインスタンスは、
    スレッドのパラメータ化されたスレッドに配置されます。ここでは、デザイン パターンに Proxy パターンを使用します。

3.3 Callable を使用したスレッドの作成

    Runnable インターフェイスとは異なり、Callable インターフェイスはスレッドの実行本体として call() メソッドを提供します。call() メソッドは run() メソッドよりも強力です。

3.4 呼び出し可能と実行可能の違い

  1.   call() メソッドには戻り値を指定できます。
  2.   call() メソッドは、例外をスローするように宣言できます。呼び出し可能インターフェース実装クラスの run メソッドでは、例外を上向きにスローできます。これは内部で処理でき、キャッチを試行できますが、実行可能インターフェース実装クラスの run メソッドの例外は例外をスローする必要があります。内部で処理され、スローすることはできません

        呼び出し可能と実行可能の両方をエグゼキュータに適用できます。スレッド クラスは実行可能のみをサポートします。

Thread の継承と Runnable インターフェイスの実装の違い

  1.  Java 設計の観点から見ると、Thread を継承してスレッドを作成するか、Runnale インターフェイスを実装するかに本質的な違いはなく、Thread は基本的に Runnable インターフェイスを実装します。
  2.  Runnbale インターフェースを実装する方法は、複数のスレッドがリソースを共有する状況により適しており、単一継承の制限を回避します。

3.5 Executorフレームワークなどのスレッドプールを利用する


    3.5.1 コンセプト

       スレッドのコンテナ、スレッドのプール

    3.5.2 利点

       1. 頻繁にスレッドを作成および破棄する操作を削減し、リソースを節約します。
       2. スレッド プールはアイドル状態のスレッドを処理して、スレッドの再利用を実現します。

    3.5.3 スレッドプール実行の基本原則

      1. プールを作成します。スレッドはありません。 pool.getPoolSize()
      2. タスクが初めてスレッド プールに送信されると、スレッド プールはタスクを実行するためのスレッドを作成します。タスクが実行されるとき。プールスレッドに返されたスレッドオブジェクトは消滅しません
      3. タスクが再度送信されると、スレッドプールはアイドル状態のスレッドを使用してタスクを処理します アイドル状態のスレッドがない場合は、新しいスレッドが作成されます

    3.5.4 スレッドプールの作成方法

        スレッド プールの作成
             ExecutorService pool = Executors.newCachedThreadPool();
             スレッド プールは int 最大スレッドまで作成できます
            ExecutorService pool = Executors.newFixedThreadPool(3);
             スレッド プール オブジェクトを作成し、
            スレッド プールを作成する最大数を指定し、必要に応じて新しいスレッドを作成しますが、利用可能な場合は以前に構築されたスレッドを再利用します
        方法 1
           1. タスク オブジェクト
                MyTarget を作成します myTarget = new MyTarget();
           2. タスクをスレッド プールに送信します
                pool.submit(myTarget);
                従来の方法 Thread t1 = new Thread( myTarget);
               短所: 自分でスレッドを作成し、タスクを実行し、死んでリソースを浪費し、システム リソースを大量に消費する
           3. スレッド プールが必要ない場合は、スレッド プールを閉じる
                pool.shutdown();
        メソッド2

            匿名内部クラス

3.5.5 スレッドプールの分類

3.5.6 スレッドプールのコアパラメータ

1. int corePoolSize

      スレッド プールのコア スレッド サイズ。タスクの数がコア スレッドの数より少ない場合でも、開いているスレッドの数は 3 のままです。

2.int 最大プールサイズ

      スレッド プール内のスレッドの最大数。K​​eepaliveTime に基づいて破棄されます。

3.キープアライブタイム

      アイドルスレッドの寿命

4. TimeUnit 時間単位

      時間単位、keepAliveTime の時間単位を指定します。 

5.workQueue ブロッキングキュー、タスクを保存するためのブロッキングキュー

        1). ArrayBlockQueue リソースの枯渇を防ぐための配列ベースの有界キュー (FIFO)

        2). LinkedBlockQueue はリンク リストに基づく無制限のブロッキング キューであり、境界値は Integer.MAX FIFO です。

                        ヒント: MaximumPoolSize パラメータは役に立ちません

        3). SynchronousBlockQueue (キャッシュタスクキューなし)

                        ヒント: MaximumPoolSize パラメーターは便利です。このパラメーターを超えると、拒否戦略が実行されます。

        4). ProprityBlockQueue() いくつかの領域でのコンパレータの実装、無制限のブロッキング キューの比較

6.スレッドファクトリー

        デーモン スレッドなどのスレッドを作成するエンジニアリング クラス

7. RejectedExecutionHandler ハンドラーの拒否戦略

        1). callerrunsPolicy (スレッドプールがシャットダウンしない限り、タスクは破棄されます)

        2). AbortPolicy (例外を直接スロー)

        3). DiscardPolicy (何もせず、タスクを直接破棄します) //推奨されません

        4).DiscardOldestPolicy (何もせず、キュー内の最も古いタスクを直接破棄します)

4. 一般的な方法

ユーザースレッド: ワーカースレッドとも呼ばれ、スレッドのタスクが実行されるか、通知によって終了します。

デーモン スレッド: 通常はワーカー スレッドとして機能します。すべてのユーザー スレッドが終了すると、デーモン スレッドは自動的に終了します。

共通デーモン スレッド: ガベージ コレクション メカニズム

5、マルチスレッド実装

 6. スレッドの終了

 7. 一般的な方法

8、スレッドのライフサイクル

8.1 スレッドの状態

 8.2 Wait()、notify()、notifyAll() の類似点

9、スレッドの安全性

 9.1 セキュリティ上の問題が発生する理由

解決

1. 同期されたコードブロック

2. 同期方法

3.ロックロック

9.2 同期済み

1. Java 言語では、共有データ操作の整合性を確保するためにオブジェクト ミューテックスの概念が導入されています。

2. 各オブジェクトは、ミューテックスと呼ばれるタグに対応しており、このタグは、常に 1 つのスレッドだけがオブジェクトにアクセスできるようにするために使用されます。

3. キーワード synchronized は、オブジェクトのミューテックスとの関連付けに使用されます。オブジェクトが synchronized で変更されると、そのオブジェクトには常に 1 つのスレッドのみがアクセスできます。

4. 同期の制限: プログラムの実行効率が低下します。

5. 同期メソッドのロック (非静的) は、このオブジェクトまたは他のオブジェクトにすることができます (同じオブジェクトである必要があります)

6. 同期メソッド (静的) のロックは現在のクラスそのものです

Java_CrazyCodeBoy のブログでの static の役割と使用法の詳細な説明 - CSDN blog_java static

 static によって変更されたメンバー変数とメンバー メソッドは、このクラスのどのオブジェクトからも独立しています。つまり、クラス固有のインスタンスに依存せず、クラスのすべてのインスタンスによって共有されます。このクラスがロードされている限り、Java 仮想マシンはデフォルトでクラス名に従ってランタイム データ領域のメソッド領域でそれらを見つけることができます。したがって、静的オブジェクトは、そのオブジェクトが作成される前でも、オブジェクトを参照せずにアクセスできます。

static キーワードによって変更された属性またはメソッドは、インスタンス化されたオブジェクトには属していませんが、クラスに属しており、これは現在の操作のインスタンス オブジェクトに属しているため、同期静的ロックはクラスに適用されます。

public class demoSell{
    //静态方法
    public synchronized static void method1(){

    }
    //静态方法中的同步代码块
    public static void mehtod2(){
        synchronized(demoSell.class){
            
        }
    }
}

知らせ:

1. 同期方法が静的で変更されていない場合: デフォルトのオブジェクトは次のとおりです。

2. メソッドが静的変更を使用する場合、デフォルトのロック オブジェクト: current class.class

3. 導入手順

       最初にロックされたコードを分析する必要があります

       同期されたコード ブロックまたは同期されたメソッドを選択します

        複数のスレッド ロック オブジェクトが同じであることを要求する

1. 同期されたコードブロック

synchronized(对象){//得到对象的锁,才能操作同步代码块
//需要被同步代码
}

2. synchronized をメソッド宣言に配置して、メソッド全体が同期メソッドであることを示すこともできます。

public synchronized void method(String name){
//需要被同步的代码
}

9.3 ロック

//创建可重复锁
Lock lock = new ReentrantLock();
//上锁
lock.lock();
//代码块
.............
//释放锁
lock.unlock();

 9.4 ねじロック

9.5 並行ツールクラス ConcurrentHashMap HashMap HashTable

9.6 スレッドのデッドロック

コンセプト:

複数のスレッドが互いのロック リソースを占有しますが、譲歩しないため、デッドロックが発生します。プログラミングでは、デッドロックを回避する必要があります。

9.7 ロックの解除

1. 現在のスレッドの同期メソッド。同期コード ブロックが実行された後、ロックは自動的に解放されます。

2. 現在のスレッドが同期コード ブロックと同期メソッドでブレークとリターンを検出します。

3. 現在のスレッドには、同期コード ブロックまたは同期メソッドに未処理のエラーまたは例外があり、その結果、異常終了が発生します。

4. 現在のスレッドは、同期コード ブロックおよび同期メソッド内のスレッド オブジェクトの wait() メソッドを実行し、現在のスレッドは一時停止してロックを解放します。

注:以下の場合はロックは解除されません。

1. スレッドが同期コード ブロックまたは同期メソッドを実行すると、プログラムは Thread.sleep() メソッドと Thread.yield() メソッドを呼び出し、ロックを解放せずに現在のスレッドの実行を一時停止します。

2. スレッドが同期コード ブロックを実行すると、他のスレッドはスレッドのサスペンド() メソッドを呼び出してスレッドを一時停止します。スレッドはロックを解放しません。スレッドの制御にサスペンド() と再開() を使用することは避けてください。 . この方法は推奨使用法ではなくなりました。

10. 考える

1. 揮発性キーワード

マルチスレッドの場合、データの可視性が保証され、volatileの属性が追加され、あるスレッドが変更した後、別のスレッドからも参照できるようになります。

原則: スレッドがデータを変更すると、そのデータがメイン メモリに更新され、他のスレッドによって読み取られたコピーは無効になり、メイン メモリ内の新しいデータが再度読み取られます。

2. count++ はアトミック操作ですか?

いいえ、次の 3 つのステップがあります。

1. メインメモリの count の値を行 A に固有の空間に格納します。

2. スレッドの固有のワークスペースで +1 操作を実行します。

3. 101 をメインメモリに再ロードしてリフレッシュします。

CPU の実行権はいつでも失われる可能性があります。たとえば、+1 演算がメイン メモリにリフレッシュされる前に、他のスレッド B によってプリエンプトされます。

3. アトミックな操作を保証する方法

1. 同期されたコードブロック

2. アトミッククラス AtomicInteger

4. 原子クラスの原則 -- CAS 原則

メインメモリの値を変更する場合は、古い値とメインメモリの値で判断してください。

        等しい場合は直接変更します

        待機しない場合は、他のスレッドによって変更されたことを意味し、メインメモリに新しい値を再取得する必要があります

3 つの変数が関係します

1. 古い値: メインメモリ内の値を初めて取得したときに、古い値 (100 など) としてこの変数に格納されます。

2. 新しい値は、スレッド操作後の値です (++ 操作実行後の 101 など)。

3. 変更する値はメインメモリ上の値です

古い値が変更される値と等しい場合、それは変更されておらず、直接割り当てることができることを意味します。

5. ConcurrentHashMap HashMap と HashTbale の違い

HashMap は最も効率的ですが安全ではありません

HashTable は効率性と安全性が最も低く、各メソッドはロックされています

ConcurrenetHashp は 2 番目に効率的で安全です

おすすめ

転載: blog.csdn.net/Doreamonx/article/details/125218129