01 Java同時プログラミング-スレッド関連概念

13633 ワード

リード1.スレッドベース概念、スレッドセキュリティ概念、複数のスレッド複数のロック概念2.オブジェクトロックの同期と非同期3.ダーティリード概念、ダーティリードビジネスシーン4.Synchronized概念、Synchronizedコードブロック、Synchronizedその他の詳細
1プロセス
スレッドといえば、まずプロセスについて話します.オペレーティングシステムでは、実行中のアプリケーションをプロセスと呼び、オペレーティングシステムがリソース(メモリ、CPUなどのリソース)を割り当て、管理する基本単位であり、完全な仮想アドレス空間を有する.
プロセスの実行を記述し、制御するために、システムはプロセス制御ブロック(PCB)と呼ばれる各プロセスのデータ構造を定義する.プロセス制御ブロックは、オペレーティングシステムに必要な、プロセスの現在の状況を記述し、プロセスの実行を制御するためのすべての情報を記録する.
プロセスの余談
オペレーティングシステムは常に多くのプロセスを実行しており、どのようにして複数のプロセスが同時に実行され、プロセスが実行される正確性を保証し、プロセスが実行されるコンテキスト環境を迅速に切り替えることができるかは、PCBのおかげです.例えば、OSがプロセスの実行をスケジュールする場合、そのプロセスのPCBからそのプロセスの現行状態と優先度を検出し、そのPCBのプログラムとデータのメモリアドレスに基づいて、そのプログラムとデータを見つける.プロセスの実行中、他のプロセスと協力する必要がある場合、同期、通信、またはファイルへのアクセスを実現する必要がある場合、PCBにもアクセスする必要があります.プロセスが何らかの理由で実行を一時停止する場合、そのブレークポイントのプロセッサ環境をPCBに保存する必要があります.
プロセス制御ブロック(PCB)に含まれる情報:
  • プロセス識別子:プロセスを一意に識別します.通常、2つあります.内部識別子と外部識別子
  • です.
  • プロセッサ状態:汎用レジスタ、命令レジスタ、プログラム状態ワード、ユーザスタックポインタ
  • を含むプロセッサ内の各種レジスタの内容.
  • プロセススケジューリング情報:プロセスステータス、プロセス優先度、イベント、スケジューリングアルゴリズムなど
  • プロセス制御情報:プログラムとデータのアドレス、プロセス同期と通信メカニズム、リソースリスト、リンクポインタ
  • プロセス間の通信方法:
  • パイプ(pipe)、有名なパイプ(named pipe):パイプは、親縁関係を有する親子プロセス間の通信に使用することができる.有名なパイプは、非親縁関係のプロセスによる通信を許可する
  • .
  • 信号(signal):信号はソフトウェアレベルで中断メカニズムのシミュレーションであり、プロセスにイベントが発生したことを通知するために使用され、1つのプロセスが1つの信号を受信し、プロセッサが1つの中断要求を受信した効果は同じと言える.
  • メッセージキュー(message queue):メッセージキューはメッセージのリンクテーブルであり、書き込み権限を持つプロセスはキューに新しいメッセージを追加し、読み取り権限を持つプロセスはメッセージキューからメッセージを取得します.通信の目的を達成する
  • 共有メモリ(shared memory):複数のプロセスが同じメモリ空間にアクセスでき、異なるプロセスは相手のプロセスにおける共有メモリ内のデータの更新をタイムリーに見ることができる.このような要求は、反発ロックや信号量
  • のような何らかの同期機構に依存する.
  • 信号量(semaphore):主にプロセスとプロセス間、同一プロセスの異なるスレッド間の同期と反発手段
  • として機能する.
  • ソケット:ネットワークの異なるマシンのプロセス間の通信
  • 3スレッド
    スレッドは軽量レベルのコンポーネントであり、オペレーティングシステムで独立してスケジューリングおよび実行できる基本単位です.プロセスで実行される演算の最小単位、すなわちプロセッサスケジューリングの基本単位です.プロセスよりも小さく、独立して実行できる基本単位です.
    プロセスを論理的にオペレーティングシステムが完了したすべてのタスクと理解すると、スレッドは、そのタスクを完了する多くの可能なサブタスクを表す.プロセス内のスレッドは、プロセス内のメモリ領域と他のシステムリソースを共有し、プロセスよりもスレッドの作成にかかるコストが小さいため、スレッドが軽量化されます.
    4プロセスとスレッドの違い
    プロセスとスレッドの主な違いは、オペレーティングシステムのリソース管理方法が異なることです.プロセスには独立したアドレス空間があり、1つのプロセスがクラッシュした後、保護モードでは他のプロセスに影響を与えませんが、スレッドは1つのプロセスの異なる実行パスにすぎません.スレッドは独自のスタック(つまりスタック)とローカル変数を持っているが、スレッドには単独のアドレス空間がなく、1つのスレッドが死ぬのはプロセス全体が死ぬのと同じであるため、マルチプロセスのプログラムはマルチスレッドのプログラムより丈夫であるが、プロセスの切り替えにかかるリソースが大きく、効率が悪い.
  • プロセスは、スレッドがない独立したアドレス空間を有する.複数のスレッド共有プロセスのメモリアドレス
  • プロセスには少なくとも1つのスレッドがあり、スレッドはプロセスに依存して存在します.スレッドは独立して実行できません.1つのプロセスに依存し、他のスレッドによって作成される必要があります.
  • スレッドとプロセスにはコンテキスト切替が存在するが、プロセス切替の代価はより大きい
  • である.
    5スレッドセキュリティ
    コンセプト
    コードが存在するプロセスで複数のスレッドが同時に実行され、これらのスレッドが同じコードになる可能性があります.各実行の結果が単一スレッドで実行された結果と同じであり、他の変数の値も予想通りであれば、スレッドは安全です.
    あるいは、1つのクラスについて、複数のスレッドがこのクラスにアクセスすると、各アクセスの結果と単一スレッドの実行結果が予想され、すなわち、各実行結果が決定され、予知されることができる.では、このクラスはスレッドが安全です.
    1つのクラスがマルチスレッド環境を行う場合でも、正しい動作を示すことができるので、このクラスはスレッドが安全です.
    スレッドセキュリティが生成される理由
    スレッドセキュリティの問題は、グローバル変数と静的変数、すなわち複数のスレッドが共有変数を予期せずに書き込み、スレッドセキュリティの問題を引き起こす.
    例を挙げる
    多くのスレッドが変数incに同時にアクセスしている場合、変数incにとってこの変数は共有変数である.スレッドが安全であるという問題が発生します.
    スレッドのセキュリティを保証するには、increaseメソッドにsynchronizedキーを付けてメソッドを同期し、1時間に1つのスレッドのみがアクセスできるようにする必要があります.
    public class Test {
        public int inc = 0;
    
        public void increase() {
            inc++;
        }
    
        public static void main(String[] args) {
            final Test test = new Test();
            for(int i=0;i<10;i++){
                new Thread(){
                    public void run() {
                        for(int j=0;j<1000;j++)
                            test.increase();
                    };
                }.start();
            }
    
            while(Thread.activeCount()>1)  //           
                Thread.yield();
            System.out.println(test.inc);
        }
    }

    6スレッド複数ロック
    まず、オブジェクトロックとは何かを理解しなければなりません.オブジェクトモニタ(Monitor)です.JavaではJvmが作成したオブジェクトごとにモニタを割り当てます.このモニタの原語は、中マルチスレッド同時環境で使用され、スレッドの秩序ある実行を保証し、1つの時点で単一スレッドのみがアクセスできるようにします.これは、synchronizedキーワードを使用して、1つの時点で1つのスレッドのみがコードブロックに入ることを許可するオブジェクトロックです.
    すなわち、複数のオブジェクトがそれぞれ自分のスレッドで実行されると、各スレッドは対応するオブジェクトのロックを有し、これらのロックは互いに干渉せず、反発することもない.ロックは異なるため、各オブジェクトは自分のロックを有している.
    次の例ではsynchronized同期は追加されていません.increaseメソッドにsynchronizedキーワードが付いている場合、この例では複数のスレッドに1つのロックがあります.この場合、後続のスレッドがincreaseメソッドを実行するには、前のスレッドが実行されるまで待たなければなりません.つまり、1時間で1つのスレッドのみがアクセスできるようになります.
    public class Test {
        public int inc = 0;
    
        public void increase() {
            inc++;
        }
    
        public static void main(String[] args) {
            final Test test = new Test();
            for(int i=0;i<10;i++){
                new Thread(){
                    public void run() {
                        for(int j=0;j<1000;j++)
                            test.increase();
                    };
                }.start();
            }
    
            while(Thread.activeCount()>1)  //           
                Thread.yield();
            System.out.println(test.inc);
        }
    }
    

    次のシーン
    コレクションVectorオブジェクトと同様に、vectorオブジェクトを2つ作成した場合は、2つのスレッドで使用します.これは、2つのスレッドがそれぞれ異なるロックを持っていることです.これが、複数のオブジェクトに対して複数のロックモデルです.
    7オブジェクトロックの同期と非同期
    中オブジェクトインスタンスのメソッドにsynchronizedキーを付ける場合をオブジェクト同期と呼びます.そうでない場合は非同期です.
    非同期では、異なるスレッドアクセスは互いに影響しません.同期は、メソッド実行に進むには、前のスレッドが実行されるのを待つ必要があります.
    8ダーティリード
    コンセプト
    ダーティリードとは、複数のスレッドがデータの読み取りまたは書き込みを操作し、読み取りスレッドが読み取ったデータが予想されないことをスレッドダーティリードと呼ぶ.
    たとえば
    スレッドT 1は、同じAに対して書き込みを行い、スレッドT 2が読み取りを担当する.書き込みスレッドT 1がAの値を読み出して修正すると、このときまだホストメモリに書き込まれておらず、CPUが切り替わると、読み出しスレッドT 2はAの値をとり、Aの古い値を取得する.このときCPUはまたT 1に戻り,新しい値をホストメモリに書き込む.このときスレッドT 1が読み取った値が最新の値と同一でない場合、スレッドダーティリードと呼ぶ.
    じょうふごう
    /**
     * Created by Gavin on 2016/10/15.
     *
     *     
     *                   ,              ,          。
     */
    public class DirtyRead {
    
        public static void main(String[] args) throws InterruptedException {
            //        
            final UserService userService = new UserService();
    
            Thread t = new Thread(new Runnable() {
                public void run() {
                    System.out.println("     :" + userService.getUser());
                    System.out.println("      ");
    
                    userService.updateUser("  ", 50);
    
                    System.out.println("      ");
                    System.out.println("     :" + userService.getUser());
                }
            });
            t.start();
    
            //           ,  100       2
            Thread.sleep(100);
    
            Thread t2= new Thread(new Runnable() {
                public void run() {
                    System.out.println("    :" + userService.getUser());
                }
            });
            t2.start();
        }
    }
    
    class UserService {
    
        private UserInfo userInfo = new UserInfo("  ", 24);
    
        public void updateUser(String name, int age) {
            userInfo.setAge(age);
    
            try {
                //   ,         ,  
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            userInfo.setUsername(name);
        }
    
        public UserInfo getUser() {
            return userInfo;
        }
    
    }
    
    /**
     *       
     */
    class UserInfo {
    
        private String username;
        private int age;
    
        public UserInfo(String username, int age) {
            this.username = username;
            this.age = age;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "UserInfo{" +
                    "username='" + username + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    実行結果:
         :UserInfo{username='  ', age=24}
          
        :UserInfo{username='  ', age=50}
          
         :UserInfo{username='  ', age=50}

    「張三」は24歳だったが、getuserでユーザー情報を取得すると「張三」は50歳になった.これが汚れ読みです.もう一つのスレッドが「張三」の情報を「リス」50歳に変更し、スレッドがまだ実行されていないため、もう一つのスレッドが情報を取得する際に発生した情報が錯乱します.
    9 Synchronizedキーワード
    コンセプト
    クラスを作成するときに、クラスコードがマルチスレッド環境で実行される可能性がある場合は、同期の問題を考慮する必要があります.Javaには言語レベルの同期原語synchronizedが内蔵されており、同じ時間に1つのスレッドしか操作できず、synchronizedに同期されたコードブロックは原子操作である.
    Synchronizedは、メソッドに宣言してもよいし、メソッド内のコードブロックに使用してもよいし、静的メソッドに使用してもよいし、synchronizedが異なるコードブロックで使用されるロックは異なる.synchronizedがメソッドに宣言された場合、モニタはメソッドが属するオブジェクトです.同期コードブロックでモニタオブジェクトをカスタマイズできるが、複数のスレッドアクセス時にモニタオブジェクトが同じであることを保証する.静的メソッドでは、モニタオブジェクトがクラスインスタンスです.
    Synchronizedアプリケーション
    Synchronizedその他
  • 再入性
  • 原子性
  • コードブロック内異常放出後、所持するロック
  • を直ちに解放する.
  • string型定数ロックは使用できません.そうしないと、永続的なブロックが発生します.