Рина Сергеева
Рина СергееваҚар. 28, 2018, 1:58 Т.Қ.

Java-да процестер мен ағындарды синхрондаудың көптеген жолдары

Предлагаю рассмотреть несколько способов синхронизации потоков и процессов, наиболее часто используемых в Java. Они отличаются реализацией и вариантами использования. Мы рассмотрим все методы на интересных примерах.

Критическая секция

Этот метод подходит вам, если:

  • параллельные потоки работают с общим ресурсом;
  • требуется синхронизация между потоками, а не процессами;

Этот метод синхронизации называется синхронизацией ресурсов («синхронизация «открыть — закрыть»). Идея этого метода заключается в том, что каждый объект в Java имеет связанный с ним монитор. Монитор — это своего рода инструмент для управления доступом к объекту.


Синхронизированный оператор используется для создания критической секции. Когда выполнение кода достигает синхронизированного оператора, монитор объекта блокируется. В момент блокировки монопольный доступ к блоку кода имеет только один поток, выполнивший блокировку. После завершения работы блока кода объектный монитор освобождается и становится доступным для других потоков.

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

Пример

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

public class Program {

    private static final Boolean key = true;                      //the object will be used for synchronization
                                                                  //it must be final 
    private static Integer amountOfGoods = 20;


    public static class Buyer implements Runnable {

        @Override
        public void run() {
            final int goodsInTheShoppingCart = (int) (1 + Math.random() * amountOfGoods);   //the number of goods that the buyer will put in the basket
            synchronized (key) {
                if (amountOfGoods - goodsInTheShoppingCart >= 0) {
                    System.out.println("The " + Thread.currentThread().getName() + " placed " + goodsInTheShoppingCart + " items in the basket.");
                    if ((amountOfGoods = amountOfGoods - goodsInTheShoppingCart) < 0) {
                        System.out.println("Whoops! The product is out of stock, but the " + Thread.currentThread().getName() + " didn't know about it.");
                        return;
                    } else {
                        System.out.println("There are " + amountOfGoods + " items left in the store.");
                    }
                } else {
                    System.out.println("The " + Thread.currentThread().getName() + " is notified that the goods are over.");
                    return;
                }
            }
            System.out.println("The " + Thread.currentThread().getName() + " made a purchase.");
        }
    }

    public static void main(String[] args) {

        for (int i = 1; i <= 5; i++) {
            Thread t = new Thread(new Buyer());              // create five threads
            t.setName("buyer " + i);
            System.out.println("The " + t.getName() + " went to the store website.");
            t.start();
        }
    }
}

Работа программы до использования блока синхронизации

Работа программы до использования блока синхронизации

Мы видим, что потоки выполняют программный код случайным образом. Из-за этого «покупатели» складывают товар в корзины, но остаются без товара.

Теперь добавим блок синхронизации и посмотрим, как изменится работа программы.

Work of the program using the synchronization unit

Теперь программа работает именно так, как мы и ожидали. Большой!

Интерфейсный канал

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

Канал представляет собой открытое соединение с объектом, таким как аппаратное устройство, файл, сетевой сокет или программный компонент, который способен выполнять одну или несколько различных операций ввода-вывода, например чтение или запись.
Канал либо открыт, либо закрыт. Канал открыт при создании и после закрытия остается закрытым. Как только канал закрыт, любая попытка вызвать для него операцию ввода-вывода вызовет исключение ClosedChannelException . Открыт ли канал, можно проверить, вызвав его методы isOpen() и tryLock(). ).

Java package.nio.channels содержит такие каналы, как:

  • AsynchronousChannel
  • ByteChannel
  • NetworkChannel
  • FileChannel
  • InterruptibleChannel
  • MulticastChannel and other.

Пример

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

Создадим два проекта, отличающихся только тем, что они будут записаны в файл — «Программа 1» или «Программа 2». Мы запускаем их одновременно.

public class Program {


    public static void main(String[] args) throws InterruptedException, IOException {

        File file = new File("C:\\text.txt");
        FileChannel fileChannel = new FileOutputStream(file, true).getChannel();

        while(fileChannel.tryLock() == null){            // the process checks if the channel is free to write to the file
            Thread.sleep(10000);
        }
        try {
            String lineSeparator = System.getProperty("line.separator");

            for (int i = 0; i < 10; i++) {

                String newData = "The program 2 is recording." + lineSeparator;
                ByteBuffer buf = ByteBuffer.allocate(200);
                buf.put(newData.getBytes());
                buf.flip();
                fileChannel.write(buf);
                Thread.sleep(1000);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        fileChannel.close();
    }
}

Если бы мы использовали классический способ записи в файл с помощью FileWriter, мы бы нашли в файле что-то похожее.

Теперь посмотрим, что будет в файле, если мы будем использовать каналы.

Это именно то, что нам нужно! Процессы получают доступ к файлу один за другим.

События

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

Это «ожидание-уведомление» синхронизации:

  • параллельные потоки ждут некоторого события;
  • один из потоков отвечает за возникновение события и уведомляет остальные потоки (один с помощью notify или все - notifyAll,) разблокируя их:
  • подходит только для синхронизации потоков, а не процессов;

Пример

Представим, как работает автоматическая система перезарядки оружия. Например, на складе всего 15 патронов. Магазин вмещает только 6. Стрелок хочет использовать все патроны.

public class Program {

    public static void main(String[] args) {

        WeaponStore weaponStore = new WeaponStore();      
        Arsenal arsenal = new Arsenal(weaponStore);
        Shooter shooter = new Shooter(weaponStore);
        new Thread(arsenal).start();
        new Thread(shooter).start();
    }
}


class WeaponStore {                                   

    private int cartridges = 0;                       

    public synchronized void shot() {                 // synchronization of the method is required
                                                      // because two threads will have access to it
        while (cartridges < 1) {
            try {
                wait();
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
                                                      //was thrown out notify by another thread, then the cartridge is received
        cartridges--;
        System.out.println("Produced 1 shot." + " Cartridges in the store: " + cartridges);
        notify();
    }

    public synchronized void reload(int arsenalCartriges) {   // synchronization of the method is required
                                                              // because two threads will have access to it
        while (cartridges >= 6) {
            try {
                wait();
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        cartridges++;
        System.out.println("1 cartridge has been added to the store." + " Cartridges in stock: " + arsenalCartriges);
        notify();
    }
}

class Arsenal implements Runnable{                     

    private int cartridges = 15;

    private WeaponStore weapon;

    Arsenal(WeaponStore weapon){
        this.weapon = weapon;
    }

    public void run(){
        for (int cartridges = 15; cartridges > 0; cartridges--) {
            weapon.reload(cartridges);
        }
    }
}


class Shooter implements Runnable{                     

    private  WeaponStore weapon;

    Shooter(WeaponStore weapon){
        this.weapon = weapon;
    }

    public void run(){
        for (int i = 1; i <= 15; i++) {
            weapon.shot();
        }
    }
}

Результат программы

1 cartridge has been added to the store. Cartridges in stock: 15 
1 cartridge has been added to the store. Cartridges in stock: 14 
1 cartridge has been added to the store. Cartridges in stock: 13 
1 cartridge has been added to the store. Cartridges in stock: 12 
1 cartridge has been added to the store. Cartridges in stock: 11 
Produced 1 shot. Cartridges in the store: 4 
Produced 1 shot. Cartridges in the store: 3 
Produced 1 shot. Cartridges in the store: 2 
Produced 1 shot. Cartridges in the store: 1 
Produced 1 shot. Cartridges in the store: 0 
1 cartridge has been added to the store. Cartridges in stock: 10 
1 cartridge has been added to the store. Cartridges in stock: 9 
1 cartridge has been added to the store. Cartridges in stock: 8 
1 cartridge has been added to the store. Cartridges in stock: 7 
1 cartridge has been added to the store. Cartridges in stock: 6 
1 cartridge has been added to the store. Cartridges in stock: 5 
Produced 1 shot. Cartridges in the store: 5 
Produced 1 shot. Cartridges in the store: 4 
Produced 1 shot. Cartridges in the store: 3 
Produced 1 shot. Cartridges in the store: 2 
Produced 1 shot. Cartridges in the store: 1 
Produced 1 shot. Cartridges in the store: 0 
1 cartridge has been added to the store. Cartridges in stock: 4 
1 cartridge has been added to the store. Cartridges in stock: 3 
1 cartridge has been added to the store. Cartridges in stock: 2 
1 cartridge has been added to the store. Cartridges in stock: 1 
Produced 1 shot. Cartridges in the store: 3 
Produced 1 shot. Cartridges in the store: 2 
Produced 1 shot. Cartridges in the store: 1 
Produced 1 shot. Cartridges in the store: 0

Вывод

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

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

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
OI
  • Ora Iro
  • Жел. 24, 2024, 6:38 Т.Ж.

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:40ұпай,
  • Бағалау ұпайлары-8
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
Соңғы пікірлер
ИМ
Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectМаусым 4, 2022, 3:49 Т.Ж.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Бізді әлеуметтік желілерде бақылаңыз