Javaマルチスレッドに関する知識【12】--設計モード--読み書きロックモード(読み書きモード)


文書ディレクトリ
  • Javaマルチスレッドに関する知識【12】--設計モード--読み書きロックモード(読み書きモード)
  • 1.問題の導入
  • 問題発生コード
  • .ソリューション
  • 解決の原理
  • コード実装
  • 読み書きロックの実装
  • 3.ステップアップソリューション
  • 問題の導入
  • 解決の原理
  • コード実装
  • 読み書きロック
  • 共有データ
  • リードスレッド
  • 書き込みスレッド
  • 読み書きロックの欠陥
  • 欠陥の分析
  • 欠陥の解決
  • 解決の実装コード
  • 読み書きロック改良
  • Javaマルチスレッドに関する知識【12】–設計モード–読み書きロックモード(読み書きモード)
    1.問題の導入
    今、観光地があると仮定して、この観光地にも切符検査員が一人しかいません.彼の仕事はお客様の切符を手に入れて、切符の副切符を引き裂いて、副切符以外の部分を観光客に返すことです.普段、客の流れはそれほど大きくないので、彼は間違いを犯すことはありません.
    しかしある日、この観光地は急に高く宣伝され、一気に多くの人がこの観光地に押し寄せた.
    客の流れが大きくなるにつれて、長い間の疲労の仕事と、この切符検査員はついに客の流れの試練に耐えられず、一部の観光客は直接切符を彼に渡したが、彼はタイムリーに切符を観光客に返さなかったが、後の観光客はまた彼の切符を渡した.
    仕事のミスで、彼は社長に給料を引かれた.
    Javaマルチスレッドでは,このような問題は,複数のスレッドが共有データを同時に操作する場合に生じる問題である可能性がある.
    問題が発生したコード
    /**
     *                
     */
    public class NoLockToWork {
        private String mainData;
        private String lastData;
        private int i=0;
        public  void check(String d,String f) throws InterruptedException {
            mainData=d;
            lastData=f;
            i++;
            verify();
        }
    
        private void verify() throws InterruptedException {
            if(!mainData.equals(lastData)){
                System.out.print("***********error*************");
                System.out.println("NO."+i+"  "+mainData+"  is  "+lastData);
                wait();
            }
    
    
        }
    
        public static void main(String[] args) {
            NoLockToWork work=new NoLockToWork();
            new Thread(()->{
                while (true){
                    try {
                        work.check("10000","10000");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(()->{
                while (true){
                    try {
                        work.check("20000","20000");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(()->{
                while (true){
                    try {
                        work.check("30000","30000");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    
    }
    

    2.ソリューション
    解決の原理
    上の問題の出現について、主管者は1つの解決方法を思いついた.すなわち、主管は切符検査員の給料を差し引いて、また複数の警備員を呼んで、園区の各位置で、同時に、2人の警備員を手配して、切符検査員の切符検査の秩序維持を助けた.
    Javaでは、対応する読み書きが必要なプロセスをロックして保護できます.
    コード実装
    読み書きロックの実装
    /**
     *             
     */
    public class AddLockToWork {
        private String mainData;
        private String lastData;
        private int i=0;
        public  void check(String d,String f) throws InterruptedException {
            mainData=d;
            lastData=f;
            i++;
            verify();
        }
    
        //      (     )
        private synchronized void verify() throws InterruptedException {
            if(!mainData.equals(lastData)){
                System.out.print("***********error*************");
                System.out.println("NO."+i+"  "+mainData+"  is  "+lastData);
                wait();
            }
    
    
        }
    
        public static void main(String[] args) {
            NoLockToWork work=new NoLockToWork();
            new Thread(()->{
                while (true){
                    try {
                        work.check("10000","10000");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(()->{
                while (true){
                    try {
                        work.check("20000","20000");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(()->{
                while (true){
                    try {
                        work.check("30000","30000");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
    

    3.ステップアップソリューション
    問題の導入
    観光地の事件について続けて言えば、園区に入ってから、一人一人が一つの観光地を見学するとき、警備員が厳格に秩序を守っているため、園区の内部の観光地ごとに毎日の観光客が急激に減少し、マネージャーを悩ませている.
    解決の原理
    マネージャーはこのような状況が発生した問題を詳しく分析した.園区の内部では、人々が観光地を見学する際、再び切符を調べる必要はないが、大量の警備員を使って秩序を維持し続けると効率が低下し、固経理は園区内の観光地前の警備を減らした.
    Javaでは、このような状況の発生は、データを読むプログラムと、データを書くプログラムとに相当し、複数人が同時に読むことができるが、同時に1人だけデータの書き込み操作を行うことができる.
    具体的な操作は以下の通りです.
    実行中のプログラムの性質
    実行するプログラムの性質
    読み取り可能
    リードロックをかけるかどうか
    書き込み可能
    書き込みロックをかけるかどうか
    リードオペレーション
    リードオペレーション
    -
    -
    書き込み操作
    -
    -
    書き込み操作
    リードオペレーション
    -
    -
    書き込み操作
    -
    -
    コード実装
    リードライトロック
    /**
     *      
     */
    public class ReadWriteLock {
        /**
         *        
         */
        private int waitReading = 0;
        /**
         *        
         */
        private int workReading = 0;
        /**
         *        
         */
        private int waitWriting = 0;
        /**
         *        (      )
         */
        private int workWriting = 0;
    
        /**
         *   ,     
         *
         * @throws InterruptedException     
         */
        public synchronized void readLock() throws InterruptedException {
            try {
                waitReading++;
                while (workWriting > 0)
                    this.wait();
                workReading++;
            } finally {
                waitReading--;
            }
    
        }
    
        /**
         *     
         */
        public synchronized void readUnLock() {
            workReading--;
            this.notifyAll();
        }
    
        /**
         *   ,     
         * @throws InterruptedException
         */
        public synchronized void writLock() throws InterruptedException {
            try {
                waitWriting++;
                while (workWriting > 0 || workReading > 0)
                    this.wait();
                workWriting++;
            } finally {
                waitWriting--;
            }
        }
    
        /**
         *     
         */
        public synchronized void writUnLock() {
            workWriting--;
            this.notifyAll();
        }
    
    }
    

    データの共有
    public class ReadWriteShareData {
        private final ReadWriteLock LOCK = new ReadWriteLock();
    
    
        private int data = 0;
    
        public void read() throws InterruptedException {
            LOCK.readLock();
            IntStream.rangeClosed(1, 10).forEach(i -> {
                if (i == 10)
                    System.out.println();
                else
                    System.out.print(data);
            });
            Thread.sleep(100);
            LOCK.readUnLock();
    
        }
    
        public void write(int data) throws InterruptedException {
            LOCK.writLock();
            this.data=data;
            LOCK.writUnLock();
        }
    
    }
    

    リードスレッド
    public class UsingReadWriteLockReader <T> extends Thread {
        private T data = null;
    
        public UsingReadWriteLockReader(T data) {
            this.data = data;
        }
    
        @Override
        public void run() {
            try {
                while (true){
                    if(data.getClass()==ReadWriteShareData.class)
                        ((ReadWriteShareData)data).read();
                    else if (data.getClass()==ReadWriteShareDataWriteFirst.class)
                        ((ReadWriteShareDataWriteFirst)data).read();
                    else
                        System.out.println("error");
                }
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    書き込みスレッド
    public class UsingReadWriteLockWriter<T> extends Thread {
    
        private T data = null;
        private int writeData = 0;
    
    
        public UsingReadWriteLockWriter(T data, int writeData) {
            this.data = data;
            this.writeData = writeData;
        }
    
    
        @Override
        public void run() {
            try {
                while (true)
                    if(data.getClass()==ReadWriteShareData.class)
                        ((ReadWriteShareData)data).write(writeData);
                    else if (data.getClass()==ReadWriteShareDataWriteFirst.class)
                        ((ReadWriteShareDataWriteFirst)data).write(writeData);
                    else
                        System.out.println("error");
            } catch (InterruptedException e) {
                // e.printStackTrace();
            }
        }
    }
    

    読み書きロックの欠陥
    欠陥の分析
    リードスレッドの限られたレベルが高いため、ライトスレッドは常にスレッドの変更ができないため、リードスレッドはファイルを占有し続け、ライトができず、データの更新ができません.
    欠陥の解決
    上記の問題を解決するために、書き込みスレッドに優先識別ビットを追加することで、関連する問題を解決できます.
    解決された実装コード
    リードライトロックの改良
    /**
     *      ,     
     */
    public class ReadWriteLockWriteFirst {
        /**
         *        
         */
        private int waitReading = 0;
        /**
         *        
         */
        private int workReading = 0;
        /**
         *        
         */
        private int waitWriting = 0;
        /**
         *        (      )
         */
        private int workWriting = 0;
        /**
         *      
         */
        private boolean writerFirst=false;
    
        public ReadWriteLockWriteFirst() {
            this(true);
        }
    
        public ReadWriteLockWriteFirst(boolean writerFirst) {
            this.writerFirst = writerFirst;
        }
    
        /**
         *   ,     
         *
         * @throws InterruptedException     
         */
        public synchronized void readLock() throws InterruptedException {
            try {
                waitReading++;
                //     
                while (workWriting > 0||(writerFirst&&waitWriting>0))
                    this.wait();
                workReading++;
            } finally {
                waitReading--;
            }
    
        }
    
        /**
         *     
         */
        public synchronized void readUnLock() {
            workReading--;
            this.notifyAll();
        }
    
        /**
         *   ,     
         * @throws InterruptedException
         */
        public synchronized void writLock() throws InterruptedException {
            try {
                waitWriting++;
                while (workWriting > 0 || workReading > 0)
                    this.wait();
                workWriting++;
            } finally {
                waitWriting--;
            }
        }
    
        /**
         *     
         */
        public synchronized void writUnLock() {
            workWriting--;
            this.notifyAll();
        }
    
    }