Рубрики
Без рубрики

Печать Четных и нечетных чисел С использованием 2 потоков

Узнайте, как синхронизировать потоки для печати четных и нечетных чисел поочередно

Автор оригинала: baeldung.

1. введение

В этом уроке мы рассмотрим, как мы можем печатать четные и нечетные числа с помощью двух потоков.

Цель состоит в том, чтобы напечатать числа по порядку, в то время как один поток печатает только четные числа, а другой поток печатает только нечетные числа. Для решения этой проблемы мы будем использовать концепции синхронизации потоков и межпотоковой связи.

2. Потоки в Java

Потоки-это легкие процессы, которые могут выполняться одновременно. Одновременное выполнение нескольких потоков может быть хорошим с точки зрения производительности и загрузки процессора, так как мы можем работать над несколькими задачами одновременно через разные потоки, работающие параллельно.

Более подробную информацию о потоках в Java можно найти в этой статье .

В Java мы можем создать поток, либо расширив класс Thread , либо реализовав Выполняемый интерфейс . В обоих случаях мы переопределяем метод run и записываем в него реализацию потока.

Более подробную информацию о том, как использовать эти методы для создания потока, можно найти здесь .

3. Синхронизация потоков

В многопоточной среде возможно, что 2 или более потоков обращаются к одному и тому же ресурсу примерно в одно и то же время. Это может привести к летальному исходу и ошибочным результатам. Чтобы предотвратить это, нам нужно убедиться, что только один поток обращается к ресурсу в данный момент времени.

Мы можем достичь этого с помощью синхронизации потоков.

В Java мы можем пометить метод или блок как синхронизированный, что означает, что только один поток сможет ввести этот метод или блок в данный момент времени.

Более подробную информацию о синхронизации потоков в Java можно найти здесь .

4. Межпоточная связь

Взаимодействие между потоками позволяет синхронизированным потокам взаимодействовать друг с другом с помощью набора методов.

Используются методы wait , notify, и notifyAll, которые все унаследованы от класса Object .

Wait() заставляет текущий поток ждать неопределенно долго, пока какой-либо другой поток не вызовет notify() или notifyAll() на том же объекте. Мы можем вызвать notify() для пробуждения потоков, ожидающих доступа к монитору этого объекта.

Более подробную информацию о работе этих методов можно найти здесь .

5. Печать нечетных и четных чисел Поочередно

5.1. Использование wait() и notify()

Мы будем использовать обсуждаемые концепции синхронизации и межпотоковой связи для печати нечетных и четных чисел в порядке возрастания с использованием двух разных потоков.

На первом шаге мы реализуем интерфейс Runnable для определения логики обоих потоков . В методе run мы проверяем, является ли число четным или нечетным.

Если число четное, мы вызываем метод print Even класса Printer , в противном случае мы вызываем метод print Odd :

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

В основном методе мы используем определенный класс для создания двух потоков. Мы создаем объект класса Принтер и передаем его в качестве параметра конструктору Задача Четная Нечетная :

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

Первый поток будет нечетным потоком, поэтому мы передаем false в качестве значения параметра isEvenNumber . Для второго потока мы передаем true вместо этого. Мы устанавливаем значение MaxValue равным 10 для обоих потоков, чтобы печатались только числа от 1 до 10.

Затем мы запускаем оба потока, вызывая метод start () . Это вызовет метод run() обоих потоков, как определено выше, в котором мы проверяем, является ли число нечетным или четным, и печатаем их.

Когда нечетный поток начнет работать, значение переменной number будет равно 1. Поскольку он меньше MaxValue , а флаг isEvenNumber равен false, вызывается printOdd () . В методе мы проверяем, является ли флаг нечетным истинным, и пока он истинен, мы вызываем wait(). Так как нечетно изначально ложно, wait() не вызывается, и значение выводится.

Затем мы устанавливаем значение is Odd в true, чтобы нечетный поток переходил в состояние ожидания и вызывал notify () , чтобы разбудить поток событий. Затем четный поток просыпается и печатает четное число, так как флаг odd является ложным. Затем он вызывает notify () , чтобы разбудить нечетный поток.

Тот же процесс выполняется до тех пор, пока значение переменной number не станет больше MaxValue .

5.2. Использование семафоров

Семафор управляет доступом к общему ресурсу с помощью счетчика. Если счетчик больше нуля, то доступ разрешен . Если он равен нулю, то доступ запрещен.

Java предоставляет класс Semaphore в пакете java.util.concurrent , и мы можем использовать его для реализации описанного механизма. Более подробную информацию о семафорах можно найти здесь .

Мы создаем два потока, нечетный поток и четный поток. Нечетный поток будет печатать нечетные числа, начиная с 1, а четный поток будет печатать четные числа, начиная с 2.

Оба потока имеют объект класса Общий принтер . Класс Общий принтер будет иметь два семафора, кажутся нечетными и кажутся четными , которые будут иметь разрешения 1 и 0 для начала . Это гарантирует, что нечетное число будет напечатано первым.

У нас есть два метода print Четное число() и print Нечетное число(). Нечетный поток вызывает метод print Odd Num() , а четный поток вызывает метод print Even Num () .

Чтобы напечатать нечетное число, метод acquire() вызывается на seem Odd , и поскольку начальное разрешение равно 1, он успешно получает доступ, печатает нечетное число и вызывает release() on semEven.

Вызов release() увеличит разрешение на 1 для seven , и тогда четный поток сможет успешно получить доступ и напечатать четное число.

Это код для рабочего процесса, описанного выше:

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. Мы рассмотрели два метода для достижения одинаковых результатов: использование wait() и notify() и использование семафора .

И, как всегда, полный рабочий код доступен на GitHub .