DEV Community

faangmaster
faangmaster

Posted on

Напечатать последовательность чисел при помощи нескольких потоков на Java

Условие. Необходимо написать программу на Java, которая напечатает последовательно числа от 0 до N — 1 (или от 1 до N) при помощи нескольких потоков.

Решение.

Для этого объявим переменную i, доступ к которой будет только после лока на одном и том же объекте для всех потоков: lock.

В synchronized блоке будем печатать и увеличивать i. Также перед печатью нужно проверить, что она еще не превысила заданное значение. Если превысила, то нужно просто выйти из функции run потока при помощи метода return.

Реализация:

    class PrintSequenceThread extends Thread {
        private static Object lock = new Object();
        private static int i = 0;
        private final int max;

        public PrintSequenceThread(int max) {
            this.max = max;
        }

        public void run() {
            while (true) {
                synchronized (lock) {
                    if (i >= max) {
                        return;
                    }
                    System.out.println(i + " " + Thread.currentThread().getName());
                    i++;
                }
            }
        }
    }

    public class Main {
        public static void main(String[] args) {
            int numberOfThreads = 4;
            int maxValueToPrint = 10;
            Thread[] threads = new Thread[numberOfThreads];
            for (int i = 0; i < numberOfThreads; i++) {
                threads[i] = new PrintSequenceThread(maxValueToPrint);
            }
            for (Thread t : threads) {
                t.start();
            }
        }
    }

    0 Thread-0
    1 Thread-0
    2 Thread-0
    3 Thread-0
    4 Thread-0
    5 Thread-0
    6 Thread-0
    7 Thread-0
    8 Thread-0
    9 Thread-0
Enter fullscreen mode Exit fullscreen mode

Несмотря на то, что такая реализация работает и она Thread Safe, у нас с большой вероятностью все числа напечатает один и тот же поток. Он получит лок, напечатает, увеличит, отпустит лок. Потом, с большой вероятность, он снова получит лок и сделает тоже самое.

Что если мы хотим, чтобы 0 напечатал первый поток, 1 второй поток и т.д. ?

В таком случае задача очень похожа на другую задачу про Ping Pong.

Будем использовать wait-notify механизм. Я подробно разбирал его тут: Методы Object и Ping Pong.

Для этого модифицируем предыдущую версию. Добавим wait на объекте lock, пока не настанет время текущим потоком печатать нужное число. Это можно проверить так:

    while (i % numberOfThreads != threadIndex) {
        try {
            lock.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
Enter fullscreen mode Exit fullscreen mode

Пока остаток от деления индекса на число потоков не равно номеру потока, мы ждем, пока работают и печатают другие потоки.

Как только наступит наша очередь, то мы напечатаем число (предварительно проверив, что оно еще не превысило максимальное), сделаем инкремент и разбудим другие потоки, чтобы они продолжили печать:

    class PrintSequenceThread extends Thread {
        private static Object lock = new Object();
        private static int i = 0;
        private final int max;
        private final int numberOfThreads;
        private final int threadIndex;

        public PrintSequenceThread(int max, int numberOfThreads, int threadIndex) {
            this.max = max;
            this.numberOfThreads = numberOfThreads;
            this.threadIndex = threadIndex;
        }

        public void run() {
            while (i < max) {
                synchronized (lock) {
                    while (i % numberOfThreads != threadIndex) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (i < max) {
                        System.out.println(i + " " + Thread.currentThread().getName());
                    }
                    i++;
                    lock.notifyAll();
                }
            }
        }
    }

    public class Main {
        public static void main(String[] args) {
            int numberOfThreads = 4;
            int maxValueToPrint = 10;
            Thread[] threads = new Thread[numberOfThreads];
            for (int i = 0; i < numberOfThreads; i++) {
                threads[i] = new PrintSequenceThread(maxValueToPrint, numberOfThreads, i);
            }
            for (Thread t : threads) {
                t.start();
            }
        }
    }

    0 Thread-0
    1 Thread-1
    2 Thread-2
    3 Thread-3
    4 Thread-0
    5 Thread-1
    6 Thread-2
    7 Thread-3
    8 Thread-0
    9 Thread-1
Enter fullscreen mode Exit fullscreen mode

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up