Рина Сергеева
Рина Сергеева28. November 2018 13:58

Mehrere Möglichkeiten zum Synchronisieren von Prozessen und Threads in Java

Ich schlage vor, mehrere Möglichkeiten zur Synchronisierung von Threads und Prozessen in Betracht zu ziehen, die am häufigsten in Java verwendeten. Sie unterscheiden sich in der Implementierung und den Anwendungsfällen. Wir werden alle Methoden mit interessanten Beispielen betrachten.

Kritischer Abschnitt

Diese Methode ist für Sie geeignet, wenn:

  • parallele Threads arbeiten mit einer gemeinsam genutzten Ressource;
  • erfordert Synchronisation zwischen Threads, nicht zwischen Prozessen;

Diese Synchronisierungsmethode wird als Ressourcensynchronisierung ("Öffnen-Schließen-Synchronisierung") bezeichnet. Die Idee hinter dieser Methode ist, dass jedem Objekt in Java ein Monitor zugeordnet ist. Ein Monitor ist eine Art Werkzeug, um den Zugriff auf ein Objekt zu kontrollieren.


Die synchronisierte Anweisung wird verwendet, um einen kritischen Abschnitt zu erstellen. Wenn die Codeausführung die synchronisierte Anweisung erreicht, wird der Objektmonitor blockiert. Zum Zeitpunkt einer Sperre hat nur ein Thread, der die Sperre erworben hat, exklusiven Zugriff auf einen Codeblock. Nachdem der Codeblock abgeschlossen ist, wird der Objektmonitor freigegeben und anderen Threads zur Verfügung gestellt.

Wenn der Monitor freigegeben wird, wird er von einem anderen Thread erfasst, und alle anderen Threads warten weiterhin darauf, dass er freigegeben wird.

Beispiel

Stellen Sie sich vor, wie ein Online-Einzelhandelsgeschäft funktioniert. Nachdem einer der Käufer das Produkt in den Warenkorb gelegt hat, soll das verbleibende Produkt gezählt werden. Erst danach kann der andere Käufer die gewünschte Menge an Waren in den Warenkorb legen. Danach kaufen Sie sie.

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

Programmablauf vor Verwendung des Synchronisationsbausteins

Programmiervorgang vor Verwendung des Synchronisationsblocks

Wir sehen, dass Threads Programmcode auf zufällige Weise ausführen. Aus diesem Grund legen "Käufer" die Waren in die Körbe, bleiben aber ohne Waren.

Lassen Sie uns nun einen Synchronisationsblock hinzufügen und sehen, wie sich das Programm ändert.

Arbeit des Programms mit der Synchronisationseinheit

Jetzt funktioniert das Programm genau so, wie wir es erwartet haben. Groß!

Schnittstellenkanal

Sie können eine Schnittstellen-Pipe verwenden, wenn Sie verschiedene Prozesse synchronisieren müssen. Der Kanal wird auf JVM-Ebene erstellt und ist im System identisch.

>Eine Pipe ist eine offene Verbindung zu einem Objekt, z. B. einem Hardwaregerät, einer Datei, einem Netzwerk-Socket oder einer Softwarekomponente, die in der Lage ist, eine oder mehrere verschiedene E/A-Operationen auszuführen, z. B. Lesen oder Schreiben.
> Der Kanal ist entweder offen oder geschlossen. Der Kanal ist geöffnet, wenn er erstellt wird, und bleibt geschlossen, nachdem er geschlossen wurde. Sobald ein Kanal geschlossen ist, löst jeder Versuch, eine E/A-Operation darauf aufzurufen, eine ClosedChannelException aus. Ob ein Kanal geöffnet ist, kann durch Aufrufen seiner Methoden isOpen() und tryLock() überprüft werden. ).

Java package.nio.channels enthält Kanäle wie:

  • Asynchroner Kanal
  • ByteKanal
  • Netzwerkkanal
  • Dateikanal
  • Unterbrechbarer Kanal
  • MulticastChannel und andere.

Beispiel

Zwei Prozesse schreiben Daten in eine Datei. Sie sollten sich bei der Aufnahme abwechseln, ohne sich gegenseitig bei der Arbeit zu stören.

Lassen Sie uns zwei Projekte erstellen, die sich nur darin unterscheiden, dass sie in eine Datei geschrieben werden - "Programm 1" oder "Programm 2". Wir betreiben sie gleichzeitig.

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

Wenn wir die klassische FileWriter-Methode zum Schreiben in eine Datei verwenden würden, würden wir etwas Ähnliches in der Datei finden.

Sehen wir uns nun an, was in der Datei enthalten sein wird, wenn wir Kanäle verwenden.

Genau das brauchen wir! Prozesse greifen nacheinander auf die Datei zu.

Entwicklungen

Zusätzlich zur Datensynchronisation gibt es eine Synchronisation nach Ereignissen. Wenn gleichzeitig laufende Threads angehalten werden, bis ein Ereignis von einem anderen Thread ausgelöst wird. Die Hauptoperationen für diese Art der Synchronisierung sind Warten und Benachrichtigen.

Dies ist die "wait-notify"-Synchronisierung:

  • parallele Threads warten auf ein Ereignis;
  • Einer der Threads ist für das Auslösen des Ereignisses verantwortlich und benachrichtigt die anderen Threads (einen mit "notify" oder alle mit "notifyAll"), indem er sie entsperrt:
  • nur zum Synchronisieren von Threads geeignet, nicht von Prozessen;

Beispiel

Stellen wir uns vor, wie das automatische Waffennachladesystem funktioniert. Zum Beispiel sind nur 15 Runden auf Lager. Das Magazin fasst nur 6. Der Schütze will die ganze Munition verbrauchen.

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

Programmergebnis

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

Fazit

Wie Sie sehen können, haben die oben besprochenen Synchronisierungsmethoden unterschiedliche Verwendungszwecke. Wählen Sie das Richtige und synchronisieren Sie Ihre Threads und Prozesse immer so, dass sie friedlich leben und sich nie gegenseitig bei der Arbeit stören.

Abonnieren Sie den Java-Bereich, wenn Sie weitere interessante Artikel wünschen. Schreiben Sie Ihre Meinung und Ideen zu diesem Thema in die Kommentare.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
A
ALO1ZE19. Oktober 2024 08:19
Fb3-Dateileser auf Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken