マルチスレッド研究ノート(2)マルチスレッドスレッド通信

前書き

人にとっては、生涯で出生、老年、病気、死などのさまざまな状態を経験します。いわゆる水は永続的ではないため、兵士は永続的ではありません。一方、糸はその後もさまざまな状態になります。スレッドが経験する状態は比較的固定されています。合計6つの状態があります。作成から終了まで、スレッドは中間の4つの状態を経験するか、毎日死ぬ可能性があります。スレッドのライフサイクル図は次のとおりです。下に示された。
ここに画像の説明を挿入

スレッドと状態

以下は、スレッドがコンピューターにロードされるときなど、コンピューター内の6つの状態のスレッドです。通常、6つの状態があります。新規、終了、時間指定待機、実行中、ブロック、待機、これら6つの状態間の関係は次のとおりです。次の:
ここに画像の説明を挿入
創造と終焉と繰り返されることはありませんが実行されている状態を通します。

時限待機

時限待機状態は時限待機状態です。Thread.sleep(xxx)を呼び出すと、時限待機状態に入ることができます。たとえば、Thread.sleep(20)は、スレッドが20msのスリープ待機状態に入るという意味です。 20ミリ秒が経過すると、スレッドは自動的にウェイクアップしてロックオブジェクトをめぐって競合します(つまり、CPUリソースの所有権をめぐって競合します)。競合が失敗すると、ブロック状態に入り、次の競合ラウンドを待ちます。
timedwaitingを入力する方法の具体例を以下に示します。

public class SleepTest {
    public static int j=200;
    public static Object lock=new Object();;
    public static void main(String[] args) throws InterruptedException {
        //Object lock = new Object();
        new Thread(){
            @Override
            public void run() {
                super.setName("赵丽颖");
                synchronized (lock){
                    for (int i = 0; i < 50; i++) {
                        j--;
                        System.out.println("这是第:"+i+"--个"+Thread.currentThread().getName()+"线程"+":"+j);
                    }
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    for (int i = 0; i < 50; i++) {
                        j++;
                        System.out.println("这是第:"+i+"--个"+Thread.currentThread().getName()+"线程"+":"+j);
                    }
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"地里热巴").start();
        Thread.sleep(100);
        System.out.println("----------"+Thread.currentThread().getName()+":----------");
    }
}

実行結果は次のとおりです。
ここに画像の説明を挿入
上記の例では、「実装」Runnableインターフェイスを使用し、Threadクラスを継承してマルチスレッドプログラムを実装し、同期テクノロジーとして同期コードブロックを使用します。これにより、スレッドセーフの問題が解決されます。

待っている

無限待機状態は待機中です。待機状態は1つのスレッドの操作ではなく、複数のスレッド間の通信を反映しています。複数のスレッド間の協調関係として理解できます。複数のスレッドがロックを争い、相互作用します。間の協力関係。AスレッドとBスレッドなど、複数のスレッドが連携している場合、AスレッドがRunnable状態でwait()メソッドを呼び出すと、Aスレッドは
Waiting(無限待機)状態になり、同期ロックを失います。この時点で、Bスレッドが同期ロックを取得し、実行状態でnotify()メソッドを呼び出すと、無期限に待機しているAスレッドがウェイクアップされます。ウェイクアップに注意してください。ロックオブジェクトが取得されると、Aスレッドはウェイクアップ後に実行可能(実行可能)状態になります。ロックオブジェクトが取得されない場合、Aスレッドはブロック(ロックブロック状態)になります。

ブロックされた

ブロックされた場合、ロックオブジェクトをめぐって競合する複数のスレッドの敗者はブロックされた状態になり、CPUリソースを取得する新しいラウンドの機会を待ちます。

スレッド間の相互通信

スレッド間の相互通信の場合、この問題はかなり広範であり、一般間のマルチスレッド通信は、共有メモリメカニズムとメッセージ通信メカニズム分けることができます。次の最初に導入された通信メカニズムのマルチスレッド共有メモリ(たとえば、java) 。

メモリ共有メカニズム

メモリ共有とは、その名前が示すように、2つのスレッドが特定のメモリ領域を共有し、共有メモリ領域のコンテンツがスレッド通信の目的を達成するために使用されることです。この方法では共有メモリがより一般的であり、共有変数を設定することがよくあります。次に、複数のスレッドが同じ共有変数を操作します。スレッド通信の目的を達成するために。
ここに画像の説明を挿入
メモリ共有メカニズムで一般的なのは、ポーリングモードと同期モードです

ポーリングモード

まず、最初のメモリ共有通信メカニズムのポーリングモードについて説明します。ポーリングモードは比較的単純です。スレッドAとBが2つある場合、プログラムAの実行後にフラグビットをトリガーする必要があります。フラグビットが一致する場合条件によりスレッドBが実行可能になり、指定されたビットが条件を満たしているかどうかをスレッドBが継続的にポーリングする必要があります。条件を満たしている場合はスレッドBのプログラムの実行を開始し、そうでない場合は実行しません。これは単に信号を実現するだけです。スレッド間の送信。小猿の場合、スレッドAが5回カウントするたびにフラグを真にし、スレッドBがフラグビットを継続的にポーリングし、条件が満たされたときにスレッドBのプログラムをトリガーするとします。
定義されたロゴクラス:

package com.itheima.demo01.polling;
public class MyFlag {
    private boolean flag ;
    public MyFlag(boolean flag) {
        this.flag = flag;
    }
    public MyFlag() {
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

スレッドAは実行可能なインターフェイスを実装し、明らかにインターフェイスにrunメソッドを実装します。

public class MyThreadA implements Runnable{
    private volatile MyFlag flag = new MyFlag();
    public MyThreadA(MyFlag flag) {
        this.flag = flag;
    }
    public MyFlag getFlag() {
        return flag;
    }
    public void setFlag(MyFlag flag) {
        this.flag = flag;
    }
    @Override
    public void run() {
        System.out.println("-----------"+Thread.currentThread().getName()+"正在执行------------------");
        while (true){
            if(!flag.isFlag()){
                for (int i = 1; i < 20; i++) {

                    System.out.println("----------------"+i+"-----------------");
                    if(i%5==0){
                        flag.setFlag(true);
                        System.out.println(flag.isFlag());
                    }else {
                        flag.setFlag(false);
                        System.out.println(flag.isFlag());
                    }
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
//        flag.setFlag(true);
//        System.out.println("-----------"+Thread.currentThread().getName()+"要退出了------------------");
    }
}

スレッドBクラスはスレッドクラスを継承し、親クラスのrunメソッドをオーバーライドします。

public class MyThreadB extends Thread {
    private volatile MyFlag flag;
    public MyFlag getFlag() {
        return flag;
    }
    public void setFlag(MyFlag flag) {
        this.flag = flag;
    }

    public MyThreadB(MyFlag flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        super.setName("MyThreadB");
        while (true){
            if(flag.isFlag()){
                System.out.println("-----------"+Thread.currentThread().getName()+"将要开始了-------------------");
                try {
                    throw  new InterruptedException("线程B需要执行了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    System.out.println("-----------"+Thread.currentThread().getName()+"将要结束了-------------------");
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

最後に、テストクラスが与えられます

public class DemoTestPolling {
    public static void main(String[] args) {
        MyFlag flag = new MyFlag(false);
        new Thread(new MyThreadA(flag),"MyThreadA").start();
        new MyThreadB(flag).start();
    }
}

ここで、MyFlagで定義されたフラグはvolatileキーワードで変更する必要があることに注意してください。これは非常に重要です。そうしないと、目的の効果が得られません。その理由は、Javaメモリモデルですべての変数がメインメモリに存在することが規定されているためです。その中で、各スレッドには独自の作業メモリーがあります。変数に対するスレッドのすべての操作は、作業メモリーで実行する必要があり、メインメモリーで直接操作することはできません。また、各スレッドは他のスレッドの作業メモリーにアクセスできません。変数の値がスレッドの作業メモリからメインメモリに書き戻されたが、スレッドAが時間内にメインメモリを更新しない場合、スレッドBはダーティリード現象を起こすため、メモリ操作の原子性、volatileキーワードを使用する必要があります変更スレッドメモリとメインメモリ間のデータ同期を確実にするため。
最終的に、Xiaoyuanの操作の結果は次のとおりです。
ここに画像の説明を挿入
ここに画像の説明を挿入

同期メカニズム

同期メカニズムも一種のメモリ共有であり、スレッドセーフの問題を解決するために広く使用されています。同期メカニズムには、同期コードブロック、同期メソッド、およびロックメカニズムが含まれます。まず、XiaoYuanが同期コードブロックの使用法を紹介します。

同期コードブロック

ケースに直接移動して、
チケット購入スレッドクラス定義します

public class RunnableImpl implements Runnable{
    //定义一个多个线程共享的票源
    private  int ticket = 1000;
    //创建一个锁对象
    Object obj = new Object();
    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操作重复执行
        while(true){
           //同步代码块
            synchronized (obj){
                //先判断票是否存在
                if(ticket>0){
                    //提高安全问题出现的概率,让程序睡眠
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //票存在,卖票 ticket--
                    System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                    ticket--;
                }
            }
        }
    }
}

テストクラス

public class Demo01Ticket {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        RunnableImpl runnable = new RunnableImpl();
        Thread thread1 = new Thread(runnable,"窗口一");
        Thread thread2 = new Thread(runnable,"窗口二");
        Thread thread3 = new Thread(runnable,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

テスト結果
ここに画像の説明を挿入ここに画像の説明を挿入
上記の場合、同期コードブロックのロックは、スレッド定義のobjクラスを使用して、チケットにアクセスする複数のスレッドによって引き起こされるスレッドセキュリティの問題を解決します。

同期方法

ケースはまだ別のウィンドウでチケットを購入しています。今回は、同期メソッドを使用してスレッドセーフの問題を解決します。
定義されたスレッドクラス

public class RunnableImpl implements Runnable{
    //定义一个多个线程共享的票源
    private static int ticket = 50;
    private  int ticket2=50;
    //设置线程任务:卖票
    @Override
    public void run() {
        System.out.println("this:"+this);
        //使用死循环,让卖票操作重复执行
        while(true){
            payTicketStatic();
            //payTicket();
        }
    }
    /*
        静态的同步方法
        锁对象是谁?
        不能是this
        this是创建对象之后产生的,静态方法优先于对象
        静态方法的锁对象是本类的class属性-->class文件对象(反射)
     */
   /* public static *//*synchronized*//* void payTicketStatic(){
        synchronized (RunnableImpl.class){
            //先判断票是否存在
            if(ticket>0){
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //票存在,卖票 ticket--
                System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                ticket--;
            }
        }
    }*/
    public static synchronized void payTicketStatic() {
        //先判断票是否存在
        if (ticket > 0) {
            //票存在,卖票 ticket--
            System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
            ticket--;
            //提高安全问题出现的概率,让程序睡眠
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
    /*
        定义一个同步方法
        同步方法也会把方法内部的代码锁住
        只让一个线程执行
        同步方法的锁对象是谁?
        就是实现类对象 new RunnableImpl()
        也是就是this
     */
    /*public *//*synchronized*//* void payTicket(){
        synchronized (this){
            //先判断票是否存在
            if(ticket>0){
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //票存在,卖票 ticket--
                System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                ticket--;
            }
        }
    }*/
    public synchronized void payTicket() {
        //先判断票是否存在
        if (ticket2 > 0) {
            //提高安全问题出现的概率,让程序睡眠
            //票存在,卖票 ticket--
            System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket2 + "张票");
            ticket2--;
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

テストクラス

public class Demo01Ticket {
    public static void main(String[] args) {
       // Runnable runnable=new RunnableImpl();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(new RunnableImpl(),"窗口一");
        Thread t1 = new Thread(new RunnableImpl(),"窗口二");
        Thread t2 = new Thread(new RunnableImpl(),"窗口三");
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

ここに画像の説明を挿入
ここで、同期ロックオブジェクトのメソッドはクラスの静的メソッド(つまり、RunnableImpl.class)であるのに対し、オブジェクトをロックする通常の同期メソッドはオブジェクト自体であることに注意してください以下に、2つの違いを示します。
ここに画像の説明を挿入
静的同期方式と同期方式の違いは、上図から反映できます。

ロック

ロックロックは、他の重要な同期メカニズムの1つです。ロックを使用するには、最初にLockオブジェクトnew ReentrantLock()を装着し、次にロックオブジェクトのメンバーメソッドlock()とunlock()を使用して問題を解決するためのコード。クリップコードのスレッドセーフ、

public class RunnableImpl implements Runnable{
    //定义一个多个线程共享的票源
    private  int ticket = 1000;
    //1.在成员位置创建一个ReentrantLock对象
    Lock l = new ReentrantLock();
    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操作重复执行
        while(true){
            //2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
            l.lock();
            //先判断票是否存在
            if(ticket>0){
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                    //票存在,卖票 ticket--
                    System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
                    l.unlock();//无论程序是否异常,都会把锁释放
                }
            }
        }
    }
    /*//设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票操作重复执行
        while(true){
           //2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
           l.lock();
            //先判断票是否存在
            if(ticket>0){
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //票存在,卖票 ticket--
                System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
                ticket--;
            }
            //3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
            l.unlock();
        }
    }*/
}

テストクラス

public class Demo01Ticket {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

運用結果
ここに画像の説明を挿入
ここに画像の説明を挿入
運用は、ケースの期待される結果を達成しました。

メッセージ通信メカニズム

メッセージパッシング方式はスレッド間の直接通信を採用しており、異なるスレッドはメッセージを明示的に送信することで相互作用の目的を達成します。メッセージパッシングの最も有名な方法は、アクターモデルです。このモデルでは、すべてがアクターであり、すべてのアクター間の通信はメッセージを渡すことによって実現する必要があります。すべてのアクターには、他のアクターから受信したメッセージを格納するための受信ボックス(メッセージキュー)があります。アクターは自分自身にメッセージを送信することもできます。もちろん、この状況は非常に特殊であり、価格比較はまれです。

ここに画像の説明を挿入

待機/通知メカニズム

Javaでは、待機/通知(NotifyAll)は非常に一般的なメッセージ通信メカニズムです。このメカニズムは、組み込みの割り込みメカニズムに少し似ています。以下では、ケースに焦点を当てます。ケースの要件は次のとおりです。割り込みがあります。 、割り込みハンドラ、CPUには3つの役割があります。割り込みが開始されると、最初に割り込みプロセッサによって処理されます。マスク不可能な割り込みの場合は、CPUによって直接処理されます。マスク可能な割り込みの場合は、最初に割り込みプロセッサによって処理され、次にCPUによって処理されます。このプロセスをコードでシミュレートするだけで、ケースコードは次のようになります。
割り込みクラス

public class Interruption {
    private String interruptType;
    private String treatment;
    private boolean interruptFlag=false;

    public Interruption(String interruptType, String treatment, boolean interruptFlag) {
        this.interruptType = interruptType;
        this.treatment = treatment;
        this.interruptFlag = interruptFlag;
    }

    public Interruption(String interruptType) {
        this.interruptType = interruptType;
    }

    public Interruption() {
    }

    public String getInterruptType() {
        return interruptType;
    }

    public void setInterruptType(String interruptType) {
        this.interruptType = interruptType;
    }

    public String getTreatment() {
        return treatment;
    }
    public void setTreatment(String treatment) {
        this.treatment = treatment;
    }
    public boolean isInterruptFlag() {
        return interruptFlag;
    }
    public void setInterruptFlag(boolean interruptFlag) {
        this.interruptFlag = interruptFlag;

    }
    @Override
    public String toString() {
        return "Interruption{" +
                "interruptType='" + interruptType + '\'' +
                ", treatment='" + treatment + '\'' +
                ", interruptFlag=" + interruptFlag +
                '}';
    }
}

割り込みハンドラ

//extends thread
public class InterruptHandler extends Thread{
    private Interruption interruption;
    public InterruptHandler(Interruption interruption,String name) {
        super(name);
        this.interruption = interruption;
    }

    public InterruptHandler(Interruption interruption) {
        this.interruption = interruption;
    }

    @Override
    public void run(){
        int count =0;
        //模拟总中断开关寄存器相关动作
        while (true){
            synchronized (interruption){
                //now there are some interrupt message in interruptHandler
                if(interruption.isInterruptFlag()){
                    try {
                        interruption.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //interruptHandler is empty
                System.out.println("interruptHandler is empty,start produce some interrupt");
                if(count%2==0){
                    interruption.setInterruptType("MI interruption");
                    interruption.setTreatment("response delay");
                }else {
                    interruption.setInterruptType("NMI interruption");
                    interruption.setTreatment("response immediately");
                }
                count++;
                System.out.println(interruption.getInterruptType()+"is readyCPU processing should be :" +
                        interruption.getTreatment());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                interruption.setInterruptFlag(true);
                interruption.notify();
            }
        }
    }
}

CPUタイプ

public class CPU implements Runnable{
    private Interruption interruption;
    private  String threadName;

    public CPU(Interruption interruption, String threadName) {
        this.interruption = interruption;
        this.threadName = threadName;
    }

    public CPU(Interruption interruption) {
        this.interruption = interruption;
    }

    @Override
    public void run() {
        while (true){
            synchronized (interruption){
                if(!interruption.isInterruptFlag()){
                    try {
                        interruption.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("CPU is processing"+interruption.getInterruptType()+"and will"+
                        interruption.getTreatment());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("finished");
                System.out.println("----------------------------------------");
                interruption.setInterruptFlag(false);
                interruption.notify();
            }
        }
    }
}

テストクラス

public class DemoTest {
    public static void main(String[] args) {
        Interruption interruption = new Interruption();
        InterruptHandler interruptHandler = new InterruptHandler(interruption,"interruptionHandler");
        interruptHandler.start();

        CPU cpu = new CPU(interruption);
        new Thread(cpu,"cpu").start();
    }
}

運用結果
ここに画像の説明を挿入
運用は、ケースの期待される結果を達成しました。

パイプライン通信メカニズム

場合

メッセージメーカーAは、メッセージを生成してパイプラインキューに入れ、キューに入れたメッセージを出力する必要があります。メッセージコンシューマーは、パイプラインキューにメッセージを抽出し、抽出したメッセージをコンソールに出力します。

方法1

PipedInputStreamとPipedOutputStreamを介してスレッド間の通信を実現するために、ThreadWriteスレッドはメッセージを生成して最初にキューに入れ、次にThreadReadスレッドがメッセージをコード上で直接消費します。
WriteDataクラス

public class WriteData {
    public void writeMethod(PipedOutputStream out){
        try {
            System.out.println("write :");
            int maxSize =50;
            StringBuffer outData1=new StringBuffer();
            String outData =null;
            for(int i=0;i<maxSize;i++){
                outData=""+(i+1);
                out.write(outData.getBytes());
                if(i==0){
                    outData1.append("["+(i+1)+",");
                }else if(i==maxSize-1){
                    outData1.append(i+1+ "]"+"\n");
                }else {
                    if((i+1)%5==0){

                        outData1.append(i+1+"]"+"\n");
                        outData1.append("[");
                    }else {
                        outData1.append(i+1+ ",");
                    }
                }
            }
            String s = outData1.toString();
            System.out.println(s);
            System.out.println();
            out.close();
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
}

ReadDataクラス

public class ReadData {
    public void readMethod(PipedInputStream inputStream){
        try {
            System.out.println("read:");
            byte[] byteArray=new byte[128];
            int readLength = inputStream.read(byteArray);
            while (readLength != -1){
                String newData = new String(byteArray, 0, readLength);
                System.out.println(newData);
                readLength=inputStream.read(byteArray);
            }
            System.out.println();
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ThreadWriteクラス

public class ThreadWrite implements Runnable{

    WriteData writeData;
    PipedOutputStream outputStream;

    public ThreadWrite(WriteData writeData,PipedOutputStream outputStream){
        this.writeData = writeData;
        this.outputStream = outputStream;
    }

    @Override
    public void run() {
        writeData.writeMethod(outputStream);
    }
}

ThreadRead

public class ThreadRead extends Thread{
    ReadData readData;
    PipedInputStream inputStream;

    public ThreadRead(ReadData readData, PipedInputStream inputStream) {
        this.readData = readData;
        this.inputStream = inputStream;
    }

    @Override
    public void run() {
        readData.readMethod(inputStream);
    }
}

テストクラス

public class TestDemo1 {
    public static void main(String[] args) {
        try {
            WriteData writeData=new WriteData();
            ReadData readData=new ReadData();
            PipedInputStream input=new PipedInputStream();
            PipedOutputStream out=new PipedOutputStream();
            out.connect(input);
            ThreadRead threadRead=new ThreadRead(readData, input);
            threadRead.start();
            Thread.sleep(2000);
            ThreadWrite threadWriteImpl=new ThreadWrite(writeData, out);
            Thread threadWrite = new Thread(threadWriteImpl,"threadWrite");
            threadWrite.start();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

テスト結果
ここに画像の説明を挿入
テスト結果は完璧です。

方法2

ケース2は、パイプ通信にPipedReaderクラスとPipedWriterクラスを使用します。メソッド2とメソッドの本質的な違いは、InputStream、OutputStrean、およびReaderとWriterの違いです。コードは次のように実装されます
。WriteDataクラス

public class WriteData {
    public void writeData(PipedWriter writer){
        int maxSize =50;
        String outData=null;
        StringBuffer outData1= new StringBuffer();
        System.out.println("write:");
        try {
            for (int i = 0; i < maxSize; i++) {
                outData=""+(i+1);
                writer.write(outData);
                if(i==0){
                    outData1.append("["+(i+1)+",");
                }else if(i==maxSize-1){
                    outData1.append(i+1+"]"+"\n");
                }else {
                    if((i+1)%5==0){
                        outData1.append(i+1+"]"+"\n");
                    }else {
                        outData1.append(i+1+",");
                    }
                }
            }
            String s = outData1.toString();
            System.out.println(s);
            writer.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

ReadDataクラス

public class ReadData {
    public void readMethod(PipedReader read){
        try {
            char [] byteArray = new char[64];
            int readLength = read.read(byteArray);
            System.out.println("read:");
            while (readLength !=-1){
                String newData = new String(byteArray, 0, readLength);
                System.out.print(newData);
                readLength = read.read(byteArray);
            }
            System.out.println();
            read.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ThreadWriterクラス

public class ThreadWriter extends Thread {
    WriteData out;
    PipedWriter writer;
    public ThreadWriter(WriteData out, PipedWriter writer) {
        this.out = out;
        this.writer = writer;
    }
    @Override
    public void run() {
        out.writeData(writer);
    }
}

テストクラス

public class DemoTest2 {
    public static void main(String[] args) {
        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();
            PipedWriter pipedWriter = new PipedWriter();
            PipedReader pipedReader = new PipedReader();
            pipedWriter.connect(pipedReader);
            ThreadWriter threadWriter = new ThreadWriter(writeData,pipedWriter);
            threadWriter.start();
            Thread.sleep(2000);
            ThreadReader threadReader = new ThreadReader(readData, pipedReader);
            Thread threadReader1 = new Thread(threadReader, "threadReader");
            threadReader1.start();
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }
    }
}

方法1と方法2を比較すると、基本的に同じであることがわかります。最大の違いは、メッセージプロデューサーがメッセージをパイプラインキューに入れる方法にあります。結果は次のとおりです。
ここに画像の説明を挿入
PipeOutputStreamは、パイプラインキューに書き込む前に、書き込まれたオブジェクトをバイナリに変換する必要がありますが、PipedWriterは変換する必要はなく、直接書き込むだけです。
試験結果:
ここに画像の説明を挿入

総括する

つまり、複数のスレッド間の通信は、メモリ共有メカニズムとメッセージパッシングメカニズムの2つのメカニズムに分けることができます。メモリ共有メカニズムには、共通の同期メカニズムとポーリングメカニズムがあり、同期メカニズムと同期コードが一般的です。ブロック、ロック;そして一般的なメッセージ配信メカニズムは待機/通知メカニズムとパイプラインメカニズムです。これらの通信方法には独自のアプリケーションシナリオがあり、マルチスレッド通信をよりカラフルで完全なものにします。XiaoYuanはここにすべての正しい批判を求めます。

おすすめ

転載: blog.csdn.net/xueshanfeitian/article/details/106558880