1.はじめに

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

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

2. Javaのスレッド

スレッドは、同時に実行できる軽量プロセスです。

複数のスレッドを同時に実行すると、パフォーマンスとCPU使用率の点で優れている可能性があります。異なるスレッドを並列に実行することで、一度に複数のタスクを処理できるからです。

Javaのスレッドの詳細については、このhttps://www.baeldung.com/java-thread-lifecycle[article]を参照してください。

  • Javaでは、

    Thread

    クラスを拡張するか、または

    Runnable

    インターフェースを実装することによってスレッドを作成できます** 。どちらの場合も、

    run

    メソッドをオーバーライドして、その中にスレッドの実装を記述します。

これらの方法を使用してスレッドを作成する方法の詳細については、https://www.baeldung.com/java-runnable-vs-extending-thread[ここ]を参照してください。

3.スレッド同期

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

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

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

Javaにおけるスレッド同期の詳細については、https://www.baeldung.com/java-synchronized[こちら]を参照してください。

4.スレッド間通信

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

使用されるメソッドは

wait



notify、

、および

notifyAll、

です。これらはすべて

Object

クラスから継承されます。


  • Wait()

    は、他のスレッドが同じオブジェクトに対して

    notify()またはnotifyAll()

    を呼び出すまで、現在のスレッドを無期限に待機させます。** このオブジェクトへのアクセスを待機しているスレッドを起こすために

    notify()

    を呼び出すことができます。モニター。

これらのメソッドの動作に関する詳細はhttps://www.baeldung.com/java-wait-notify[ここ]にあります。

5.奇数と偶数を代わりに印刷する

5.1.

wait()



notify()

を使う

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

  • 最初のステップでは、両方のスレッドのロジックを定義するために

    Runnable

    インターフェースを実装します** 。

    run

    メソッドでは、数値が偶数か奇数かを確認します。

数が偶数の場合は、

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();
    }
}

  • mainメソッドでは、定義済みクラスを使用して2つのスレッドを作成します**

    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();
}

最初のスレッドは奇数スレッドになるので、

fELL

をパラメーター

isEvenNumber

の値として渡します。 2番目のスレッドでは、代わりに

true

を渡します。両方のスレッドで

maxValue

を10に設定して、1から10までの数字だけが印刷されるようにします。

その後、

start()

メソッドを呼び出して両方のスレッドを起動します。これは上で定義されたように両方のスレッドの

run()

メソッドを呼び出します、そこで我々は数が奇数であるか偶数であるかをチェックしてそれらを表示します。

奇数スレッドが実行を開始すると、変数

number

の値は1になります。これは

maxValue

より小さく、フラグ

isEvenNumber

がfalseであるため、

printOdd()

が呼び出されます。このメソッドでは、

isOdd

フラグがtrueであるかどうかをチェックし、trueの間に

__wait()を呼び出します。

isOdd

は最初はfalseであるため、

wait()__は呼び出されず、値が表示されます。

  • 次に、

    isOdd

    の値をtrueに設定して、奇数スレッドが待機状態になり、

    notify()

    ** を呼び出して偶数スレッドを起動します。次に、

    odd

    フラグがfalseであるため、偶数スレッドが起動して偶数を出力します。それから、__notify()を呼び出して、奇数スレッドを起こします。

変数

number

の値が

maxValue

より大きくなるまで、同じプロセスが実行されます。

5.2. セマフォの使い方

セマフォは、カウンタを使用して共有リソースへのアクセスを制御します。

カウンタが0より大きい場合、アクセスは許可されます

ゼロの場合、アクセスは拒否されます。

Javaは

java.util.concurrent

パッケージに

Semaphore

クラスを提供しており、それを使って説明したメカニズムを実装することができます。セマフォの詳細については、https://www.baeldung.com/java-semaphore[here]を参照してください。

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

どちらのスレッドも

SharedPrinter

クラスのオブジェクトを持っています。 **

SharedPrinter

クラスには、

semOdd

と____semEvenの2つのセマフォがあります。これにより、奇数が最初に印刷されます。


printEvenNum()



__printOddNum()の2つのメソッドがあります。


奇数スレッドは

printOddNum()

メソッドを呼び出し、偶数スレッドは

printEvenNum()__メソッドを呼び出します。

奇数を出力するには、

sequire()

メソッドを

semOdd

で呼び出します。最初の許可は1なので、アクセスを正常に取得し、奇数を出力して

semEven.



release()

を呼び出します。


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つのスレッドを使用して奇数と偶数を交互に印刷する方法について説明しました。同じ結果を得るための2つの方法を見てみました。

そしていつものように、完全なワーキングコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-concurrency[GitHubで利用可能]で利用可能です。