Java生産者消費者モデル

10426 ワード

Javaでのスレッド同期の古典的な例では、異なるスレッドは同じオブジェクトに対して同時にマルチスレッド操作を行い、スレッドの安全を維持するために、データ結果は私たちが望む結果である.
生産者-消費者モデルはこの現象をよく説明することができる:共通データdataに対して、初期値は0で、複数のスレッドはそれを増加または減少させるが、私たちの目的はいくつかのスレッドが同時に彼を操作しても、結果は:data=0の時、増加するしかなく、data=1の時に減少するしかない.
コードが簡単なので、すべてのクラスを同じクラスに書いて、静的な内部クラスの形で現れ、紙幅を節約します.
  1.スレッドが安全ではありません:
public class Test {

    public static void main(String[] args) {

        Resouce resouce = new Resouce();

        for(;;) {

            new Thread(new Run1(resouce)).start();

            new Thread(new Run2(resouce)).start();

        }

    }



    static class Run1 implements Runnable {

        private Resouce resouce;



        public Run1(Resouce resouce) {

            this.resouce = resouce;

        }



        public void run() {

            resouce.add();

        }

    }



    static class Run2 implements Runnable {

        private Resouce resouce;



        public Run2(Resouce resouce) {

            this.resouce = resouce;

        }



        public void run() {

            resouce.minus();

        }

    }



    static class Resouce {

        public int data = 0;



        public void add() {

            data++;

            System.out.println(data);

        }



        public void minus() {

            data--;

            System.out.println(data);

        }

    }

}

: 0、1 , 。

  2.スレッドのセキュリティ:
public class Test {

    public static void main(String[] args) {

        Resouce resouce = new Resouce();

        for(;;) {

            new Thread(new Run1(resouce)).start();

            new Thread(new Run2(resouce)).start();

        }

    }



    static class Run1 implements Runnable {

        private Resouce resouce;



        public Run1(Resouce resouce) {

            this.resouce = resouce;

        }



        public void run() {

            resouce.add();

        }

    }



    static class Run2 implements Runnable {

        private Resouce resouce;



        public Run2(Resouce resouce) {

            this.resouce = resouce;

        }



        public void run() {

            resouce.minus();

        }

    }



    static class Resouce {

        public int data = 0;



        public synchronized void add() {

            while (data != 0) {

                try {

                    wait();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

            data++;

            System.out.println(data);

            notify();

        }



        public synchronized void minus() {

            while (data == 0) {

                try {

                    wait();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

            data--;

            System.out.println(data);

            notify();

        }

    }

}

:0、1 , 。

次の情報について説明します.
  1.JDKソースコードにより、new Thread(new Runnable(){@override run(){}}){@override run(){}}}について発見することができる.start();このような形式では、事実上、1つのThreadのサブクラスをnewしてstart()を呼び出すとともに、パラメータはまたRunnableインタフェースを実現したクラスの例であり、両者ともrun()メソッドを書き換えているので、このときrun()を書き換えると、new Thread(){}という中のrunメソッドが呼び出され、new Thread(){@override run(){}}のみであれば.start();このような形式では、パラメータ内のrunメソッドが呼び出されます.Threadのソースコードを参照してください.
    public void run() {

    if (target != null) {

        target.run();

    }

    }

Threadのrunメソッドが呼び出され、このときThreadのrunメソッドが書き換えられず、targetがnullでない場合、targetのrunメソッドが呼び出され、targetはパラメータが伝達されたRunnableインタフェースの実装クラスである.
  2.上の第1点でThreadのrunメソッドを補足するとパラメータのrunメソッドが呼び出されるので、mainメソッドをテストします.
    public static void main(String[] args) {

        Resouce resouce = new Resouce();

        for(;;) {

            new Thread(new Run1(resouce)).start();

            new Thread(new Run2(resouce)).start();

        }

    }

パラメータ(Run 1とRun 2)のrunメソッドが呼び出され、同時にRun 1とRun 2のrunメソッドのそれぞれにおいてResouceクラスのaddとminusメソッドが呼び出され、Run 1とRun 2の両方にReasourceというオブジェクトの参照を持たせ、同一の参照とすることで、複数のスレッドが同時にResourceを操作する同一のインスタンスが実現される.そして論理(ここでaddとminusの2つの同期方法)はResourceに書かれています.
  3.addとminusの2つの同期方法の中で、addまたはminus waitがしばらく起動されると、whileブロックの下のコード、つまり
            data++;

            System.out.println(data);

            notifyAll();

または
            data--;

            System.out.println(data);

            notifyAll();

この2つのうちの1つは、このときwhileを使用して起動されたと判断し、ifを使用してもよいが、ここではwhileを使用することが望ましい.スレッドが起動されると、whileはwhileの条件を再検査し、満たさなければ睡眠を続け、ifは直接次のコードを実行するからだ.以前、張孝祥先生が待っているスレッドが偽で起動される可能性があると聞いていたのを覚えています.偽で起動された場合、while条件をチェックしないと、深刻な問題が発生するので、ここではwhileを使います.JDKの原語解釈は、「あるパラメータのバージョンに対して、割り込みと虚偽の目覚ましを実現することは可能である」とし、はっきり言ってnotifyやnotifyAllに目覚ましされていない可能性があり、両者に目覚ましされていなければ、実行を継続させることはできない.