2つのスレッドを使用して偶数と奇数を印刷する

2つのスレッドを使用して偶数と奇数を印刷する

1. 前書き

このチュートリアルでは、2つのスレッドを使用して偶数と奇数を印刷する方法を見ていきます。

目標は、番号を順番に印刷することです。一方のスレッドは偶数のみを印刷し、もう一方のスレッドは奇数のみを印刷します。 この問題を解決するために、スレッド同期とスレッド間通信の概念を使用します。

2. Javaのスレッド

スレッドは、同時に実行できる軽量のプロセスです。 複数のスレッドを同時に実行すると、パフォーマンスとCPU使用率の点で優れている場合があります。これは、並行して実行される異なるスレッドを介して複数のタスクを一度に処理できるためです。

Javaのスレッドの詳細については、このarticleを参照してください。

In Java, we can create a thread by either extending the Thread class or by implementing the Runnable interface。 どちらの場合も、runメソッドをオーバーライドし、その中にスレッドの実装を記述します。

これらのメソッドを使用してスレッドを作成する方法の詳細については、hereを参照してください。

3. スレッド同期

マルチスレッド環境では、2つ以上のスレッドがほぼ同時に同じリソースにアクセスしている可能性があります。 これは致命的で、誤った結果につながる可能性があります。 これを防ぐには、特定の時点で1つのスレッドのみがリソースにアクセスするようにする必要があります。

これは、スレッド同期を使用して実現できます。

Javaでは、メソッドまたはブロックを同期済みとしてマークできます。つまり、特定の時点でそのメソッドまたはブロックに入ることができるのは1つのスレッドだけです。

Javaでのスレッド同期の詳細については、hereを参照してください。

4. スレッド間通信

スレッド間通信では、一連のメソッドを使用して、同期されたスレッドが相互に通信できます。

使用されるメソッドは、waitnotify,、およびnotifyAll,であり、これらはすべてObjectクラスから継承されます。

Wait() causes the current thread to wait indefinitely until some other thread calls notify() or notifyAll() on the same object.notify()を呼び出して、このオブジェクトのモニターへのアクセスを待機しているスレッドをウェイクアップできます。

これらのメソッドの動作の詳細については、hereを参照してください。

5. 奇数と偶数を交互に印刷する

5.1. wait()およびnotify()の使用

ここで説明した同期とスレッド間通信の概念を使用して、2つの異なるスレッドを使用して奇数と偶数を昇順で印刷します。

In the first step, we’ll implement the Runnable interface to define the logic of both threadsrunメソッドでは、数値が偶数か奇数かを確認します。

数値が偶数の場合は、PrinterクラスのprintEvenメソッドを呼び出します。それ以外の場合は、printOddメソッドを呼び出します。

class TaskEvenOdd implements Runnable {
    private int max;
    private Printer print;
    private boolean isEvenNumber;

    // standard constructors

    @Override
    public void run() {
        int number = isEvenNumber ? 2 : 1;
        while (number <= max) {
            if (isEvenNumber) {
                print.printEven(number);
            } else {
                print.printOdd(number);
            }
            number += 2;
        }
    }
}

Printerクラスを次のように定義します。

class Printer {
    private volatile boolean isOdd;

    synchronized void printEven(int number) {
        while (!isOdd) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        System.out.println(Thread.currentThread().getName() + ":" + number);
        isOdd = false;
        notify();
    }

    synchronized void printOdd(int number) {
        while (isOdd) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        System.out.println(Thread.currentThread().getName() + ":" + number);
        isOdd = true;
        notify();
    }
}

In the main method, we use the defined class to create two threads.Printerクラスのオブジェクトを作成し、それをパラメーターとしてTaskEvenOddコンストラクターに渡します。

public static void main(String... args) {
    Printer print = new Printer();
    Thread t1 = new Thread(new TaskEvenOdd(print, 10, false),"Odd");
    Thread t2 = new Thread(new TaskEvenOdd(print, 10, true),"Even");
    t1.start();
    t2.start();
}

最初のスレッドは奇数スレッドになるため、パラメーターisEvenNumberの値としてfalseを渡します。 2番目のスレッドでは、代わりにtrueを渡します。 両方のスレッドでmaxValueを10に設定して、1から10までの数字のみが出力されるようにします。

次に、start()メソッドを呼び出して両方のスレッドを開始します。 これにより、上記で定義した両方のスレッドのrun()メソッドが呼び出され、数値が奇数か偶数かを確認して出力します。

奇数スレッドが実行を開始すると、変数numberの値は1になります。 maxValue未満であり、フラグisEvenNumberがfalseであるため、printOdd()が呼び出されます。 このメソッドでは、フラグisOddがtrueであるかどうかを確認し、trueの場合はwait(). を呼び出します。isOddは最初はfalseであるため、wait()は呼び出されず、値は印刷されます。

We then set the value of isOdd to true, so that the odd thread goes into the wait state and call notify()は、偶数スレッドをウェイクアップします。 次に、oddフラグがfalseであるため、偶数スレッドがウェイクアップして偶数を出力します。 次に、notify()を呼び出して、奇数スレッドをウェイクアップします。

変数numberの値がmaxValueより大きくなるまで、同じプロセスが実行されます。

5.2. セマフォの使用

セマフォは、カウンターを使用して共有リソースへのアクセスを制御します。 counter is greater than zero, then access is allowedの場合。 ゼロの場合、アクセスは拒否されます。

Javaはjava.util.concurrentパッケージでSemaphoreクラスを提供し、それを使用して説明されたメカニズムを実装できます。 セマフォの詳細については、hereを参照してください。

奇数スレッドと偶数スレッドの2つのスレッドを作成します。 奇数スレッドは1から始まる奇数を出力し、偶数スレッドは2から始まる偶数を出力します。

両方のスレッドには、SharedPrinterクラスのオブジェクトがあります。 The SharedPrinter class will have two semaphores, semOdd and semEven which will have 1 and 0 permits to start with。 これにより、奇数が最初に印刷されます。

printEvenNum()printOddNum(). の2つのメソッドがあります。奇数スレッドはprintOddNum()メソッドを呼び出し、偶数スレッドはprintEvenNum()メソッドを呼び出します。

奇数を出力するには、semOddacquire()メソッドを呼び出し、初期許可が1であるため、アクセスを正常に取得し、奇数を出力して、release()semEven.

release()を呼び出すと、semEvenの許可が1ずつ増加し、偶数スレッドはアクセスを正常に取得して偶数を出力できます。

これは、上記のワークフローのコードです。

public static void main(String[] args) {
    SharedPrinter sp = new SharedPrinter();
    Thread odd = new Thread(new Odd(sp, 10),"Odd");
    Thread even = new Thread(new Even(sp, 10),"Even");
    odd.start();
    even.start();
}
class SharedPrinter {

    private Semaphore semEven = new Semaphore(0);
    private Semaphore semOdd = new Semaphore(1);

    void printEvenNum(int num) {
        try {
            semEven.acquire();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println(Thread.currentThread().getName() + num);
        semOdd.release();
    }

    void printOddNum(int num) {
        try {
            semOdd.acquire();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println(Thread.currentThread().getName() + num);
        semEven.release();

    }
}

class Even implements Runnable {
    private SharedPrinter sp;
    private int max;

    // standard constructor

    @Override
    public void run() {
        for (int i = 2; i <= max; i = i + 2) {
            sp.printEvenNum(i);
        }
    }
}

class Odd implements Runnable {
    private SharedPrinter sp;
    private int max;

    // standard constructors
    @Override
    public void run() {
        for (int i = 1; i <= max; i = i + 2) {
            sp.printOddNum(i);
        }
    }
}

6. 結論

このチュートリアルでは、Javaの2つのスレッドを使用して、奇数と偶数を交互に出力する方法を確認しました。 同じ結果を達成するために、using wait() and notify()using a Semaphore.の2つの方法を検討しました。

そして、いつものように、完全に機能するコードはover on GitHubで利用できます。