スレッド/プロセス通信

プロセス間の通信

  1. パイプ(パイプ):パイプは半二重通信方式であり、データは一方向にのみ流れることができ、血液関係のあるプロセス間でのみ使用できます。プロセスの血縁関係は通常、父親と息子のプロセス関係を指します
  2. 名前付きパイプ:名前付きパイプも半二重通信方式ですが、無関係なプロセス通信を可能にします
  3. セマフォ(セモフォア):セマフォは、共有リソースへの複数のプロセスのアクセスを制御するために使用できるカウンターです。通常、他のプロセスもリソースにアクセスしているときに、プロセスが共有リソースにアクセスするのを防ぐためのロックメカニズムとして使用されます。したがって、それは主にプロセス間および同じプロセス内の異なるスレッド間の同期方法として使用されます
  4. メッセージキュー(メッセージキュー):メッセージキューは、カーネルに格納され、メッセージキュー識別子によって識別されるメッセージのリンクリストです。メッセージキューは、信号伝送情報が少ないという欠点を克服し、パイプラインはフォーマットされていないバイトストリームしか伝送できず、バッファーサイズが制限されます。
  5. シグナル(signal):シグナルはより複雑な通信方法であり、イベントが発生したことを受信プロセスに通知するために使用されます
  6. 共有メモリ(共有メモリ):共有メモリは、他のプロセスからアクセスできるメモリの一部です。この共有メモリは、1つのプロセスによって作成されますが、複数のプロセスがアクセスできます。共有メモリは、最速のIPCメソッドです。他のプロセス間の通信方法は、非効率的に実行されるように特別に設計されています。プロセス間の同期と通信を実現するために、セマフォなどの他の通信メカニズムと併用されることがよくあります。
  7. ソケット(socket):ソケットはプロセス間の通信メカニズムでもあり、他の通信メカニズムとは異なり、異なるプロセス間通信に使用できます。
  8. メモリマッピング:メモリマッピングにより、複数のプロセス間の通信が可能になります。このメカニズムを使用する各プロセスは、共有ファイルを独自のプロセスアドレス空間にマッピングすることで実装します

スレッド間の通信メカニズム

  スレッド間の通信には、共有メモリとメッセージパッシングの 2つのモデルがあります

题目:有两个线程ABA线程向一个集合里面依次添加元素
"abc"字符串,一共添加十次,当添加到第五次的时候,
希望B线程能够收到A线程的通知,然后B线程执行相关的业务操作。

方法1:volatileキーワードを使用する

  共有メモリを使用して、volatileキーワードに基づいてスレッド間の相互通信を実現するという考え方です。つまり、複数のスレッドが同時に変数をリッスンします。この変数が変化すると、スレッドは対応するビジネスを認識して実行できます。これは、達成する最も簡単な方法でもあります

public class TestSync {
    // 定义一个共享变量来实现通信,它需要是volatile修饰,否则线程不能及时感知
    static volatile boolean notice = false;
    public static void main(String[] args) {
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    notice = true;
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                if (notice) {
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                    break;
                }
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}

方法2:Objectクラスのwait()およびnotify()メソッドを使用する

  Objectクラスは、スレッド間の通信のためのメソッドを提供します:wait()、notify()、notifyaAll()、これらはマルチスレッド通信の基礎であり、この実装のアイデアは当然スレッド間通信です。注:待機と通知は同期して使用する必要があります。待機メソッドはロックを解放し、通知メソッドはロックを解放しません。

public class TestSync {
    public static void main(String[] args) {
        // 定义一个锁对象
        Object lock = new Object();
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                for (int i = 1; i <= 10; i++) {
                    list.add("abc");
                    System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (list.size() == 5)
                        lock.notify();// 唤醒B线程
                }
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                synchronized (lock) {
                    if (list.size() != 5) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                }
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}

スレッドAがnotify()ウェイクアップ通知を発行した後、スレッドBは独自のスレッドのビジネスを実行した後でのみ実行を開始します。これは、wait()メソッドがロックを解放している間、notify()メソッドがロックを解放しないことも示しています。

方法3:JUCツールクラスCountDownLatchを使用する

  JDK1.5はjava.util.concurrentパッケージで並行プログラミングに関連する多数のツールを提供した後、たCountDownLatch AQSフレームワークをベースとするだけでなく、状態変数間で共有スレッドの等価性を維持するために

public class TestSync {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    countDownLatch.countDown();
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                if (list.size() != 5) {
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程B收到通知,开始执行自己的业务...");
                break;
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}

方法4:条件でReentrantLockを使用する

public class TestSync {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        List<String> list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            lock.lock();
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    condition.signal();

            }
            lock.unlock();
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            lock.lock();
            if (list.size() != 5) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程B收到通知,开始执行自己的业务...");
            lock.unlock();
        });
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadA.start();
    }
}

このようにコードを記述するとコードが複雑になるだけでなく、スレッドBはロックを取得しないため、つまりAがウェイクアップ操作の後にロックを解放しないため、Aによってウェイクアップされた直後に実行できません。このメソッドは、オブジェクトの待機()および通知()と同じです。

方法5:スレッド間のブロッキングとウェイクアップを実現する基本的なLockSupport

  LockSupportは、スレッド間でブロックおよび起動するための非常に柔軟なツールです。スレッドが最初に実行されるのを待つか、最初に実行するためにスレッドを起動するかを気にせずに使用しますが、スレッドの名前を知っている必要があります。

public class TestSync {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // 实现线程B
        final Thread threadB = new Thread(() -> {
            if (list.size() != 5) {
                LockSupport.park();
            }
            System.out.println("线程B收到通知,开始执行自己的业务...");
        });
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    LockSupport.unpark(threadB);
            }
        });
        threadA.start();
        threadB.start();
    }
}

———————————————————————————————————————

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

  プロセスは、システムによるリソースの割り当てとスケジューリングのための独立したユニットです。
  スレッドはプロセスのエンティティであり、CPUのスケジューリングとディスパッチの基本単位であり、プロセスよりも小さく、独立して実行できる基本単位です。スレッドには独自のスタックとローカル変数があります。スレッドには独自のスタックスペース(プログラムカウンター、一連のレジスタ、スタックなど)がありますが、プロセスが所有するすべてのリソースを、同じプロセスに属する他のスレッドと共有できます。

違い
  プロセスとスレッドの主な違いは、オペレーティングシステムのリソース管理の方法が異なることです。プロセスには独立したアドレススペースがあります。プロセスがクラッシュしても、プロテクトモードの他のプロセスには影響せず、スレッドはプロセス内の単なる異なる実行パスです。スレッドには独自のスタック変数とローカル変数があり、独立した実行シーケンスがありますが、スレッド間に個別のアドレス空間はありません。スレッドの終了は、プロセス全体の終了と同等であるため、マルチプロセスプログラムはマルチスレッドプログラムよりも堅牢です。プロセスが切り替えられると、より多くのリソースが消費され、効率が低下します。ただし、特定の変数の同時実行と共有を必要とする並行操作の場合は、プロセスではなくスレッドのみを使用できます。

162の元の記事を公開 58を賞賛 90,000ビュー

おすすめ

転載: blog.csdn.net/ThreeAspects/article/details/105539033