Multithreading und parallele Programmierung

Ausführbares Objekt : Es kann als Aufgabe verstanden werden und ist eine Instanz der Runnable-Schnittstelle.

Thread : Der Ausführungsprozess einer Aufgabe von Anfang bis Ende. Es handelt sich im Wesentlichen um ein Objekt, das die Ausführung einer Aufgabe erleichtert.

Thread-Status : Der Thread-Status ist eine Phase des Thread-Lebenszyklus im Betriebssystem.

Prozess : Eine Anwendung, die im Speicher ausgeführt wird. In einem Prozess können mehrere Threads gestartet werden.

Aufgabenklassen enthalten im Allgemeinen einige Attribute und Methoden, mit denen der Status und das Verhalten der Aufgabe beschrieben werden können.

Unten finden Sie ein Beispiel für die Definition einer Aufgabenklasse und das Erstellen eines Threads.

public class Task implements Runnable {
    private String name;
    public Task(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        // 执行任务
        System.out.println("Executing task: " + name);
    }

    public static void main(String[] args) {
        // 创建一个任务
        Task task = new Task("Task 1");
        // 创建一个线程来执行任务
        Thread thread = new Thread(task);
        // 启动线程
        thread.start();
    }
}

Nach dem Aufruf der Startmethode wird die Ausführungsmethode zu einer normalen Methode, die Thread standardmäßig ausführt.

Ich denke, der Test...

Korrektur von Programmfehlern

1. forFun Die Klasse implementiert  Runnable die Schnittstelle,  forFun erstellt jedoch im Konstruktor ein neues  forFun Objekt und versucht, es in einem neuen Thread auszuführen.  Dieses neue Objekt implementiert die Schnittstelle  jedoch  forFun nicht  und kann daher nicht als  an   den Konstruktor übergeben werdenRunnableRunnableThread

public class forFun implements Runnable {
    public static void main(String[] args) {
        new forFun();
    }
    public forFun(){
        new Thread(this).start();
    }

    @Override
    public void run() {
        System.out.println("1");
    }
}

Auf diese Weise wird der Test fünfmal ausgegeben

2. Start zweimal aufgerufen

Jeder Thread kann nur einmal gestartet werden, löschen Sie einfach einen Start.

Was ist der Zweck der Verwendung von Platform.runLater ?

Weist das System an, das Runnable-Objekt im Anwendungsthread auszuführen.

★Thread-Status (dies muss getestet werden)

  1. Neu : Wenn ein Thread erstellt wird, aber noch nicht mit der Ausführung begonnen hat, befindet er sich im neuen Status.

  2. Ausführbar : Der Thread ist zur Ausführung bereit, wartet jedoch möglicherweise auf die CPU-Planung.

  3. Läuft : Der Thread wird auf der CPU ausgeführt.

  4. Blockiert : Der Thread wartet auf ein Ereignis (z. B. den Abschluss eines E/A-Vorgangs), sodass er die Ausführung vorübergehend stoppt und auf das Eintreten des Ereignisses wartet.

  5. Warten : Ein Thread wartet darauf, dass ein anderer Thread eine Operation ausführt.

  6. Zeitgesteuertes Warten : Ein Thread wartet darauf, dass ein anderer Thread einen Vorgang ausführt, es gibt jedoch ein Zeitlimit.

  7. Beendet : Der Thread hat die Ausführung abgeschlossen oder wurde von anderen Threads unterbrochen.

        Wenn wir einen neuen Thread erstellen, befindet er sich zunächst im neuen Zustand. Wenn wir dann die start()-Methode aufrufen, beginnt der Thread mit der Ausführung und wechselt in den Runnable-Status. Wenn der CPU-Scheduler die Ausführung dieses Threads auswählt, wechselt er in den Ausführungsstatus. Wenn der Thread während des Betriebs auf ein Ereignis stößt, auf das gewartet werden muss (z. B. eine E/A-Operation), wechselt er in den blockierten Zustand. Wenn das Warteereignis abgeschlossen ist, wechselt der Thread wieder in den ausführbaren Zustand und wartet auf die Auswahl des CPU-Schedulers. Wenn es aktiv wartet, wechselt es in den Wartezustand, wartet darauf, geweckt zu werden, und wechselt dann wieder in den ausführbaren Zustand. Wenn es aktiv schläft, wechselt es in den Wartezustand und nach Ablauf der Ruhezeit in den ausführbaren Zustand. Beachten Sie, dass bei einer abnormalen Unterbrechung des Betriebszustands oder nach Abschluss der Ausführung in die Totphase eintritt.

( Sehen Sie sich das Bild an, um Ihr Verständnis zu vertiefen. Das Obige ist meine eigene Zusammenfassung .)

Welche Vorteile bietet die Verwendung eines Thread-Pools?

Durch die Verwendung von Thread-Pools können Threads effektiv verwaltet und geplant, die Parallelitätsleistung des Systems verbessert und gleichzeitig Systemressourcen effektiv verwaltet, eine Erschöpfung der Systemressourcen vermieden und die Stabilität und Reaktionsfähigkeit des Systems verbessert werden.

Sperren

Unterteilt in zwei Typen: synchronisierte Sperre und wiedereintretende Sperre ReentrantLock

1. Synchronisationssperre

public class LockExample {
    private final Object lock = new Object();

    public void method() {
        synchronized (lock) {
            // 这里是临界区,只有一个线程可以进入
            // 其他线程需要等待这个线程释放锁
        }
    }
}

2. Wiedereintrittssperre

 Derselbe Thread kann diese Sperre mehrmals erwerben. Sie müssen jedoch daran denken, die Sperre rechtzeitig aufzuheben, um einen Deadlock zu vermeiden.

import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final ReentrantLock lock = new ReentrantLock();
    public void method() {
        lock.lock();
        try {
            // 这里是临界区,只有一个线程可以进入
            // 其他线程需要等待这个线程释放锁
        } finally {
            lock.unlock();
        }
    }
}

Es gibt eine andere Möglichkeit, es zu beschreiben

1. Private statische Sperre lock=new ReentrantLock();

2. Holen Sie sich lock.lock()

3. Lock.unlock() freigeben; 

4. Erstellen Sie eine Sperrbedingung lock.newCondition()

wait() gibt die Sperre frei und unterbricht den Thread. Sobald die Bedingungen erfüllt sind, wird es aktiviert und erhält die Sperre erneut.

signal()/signalAll(): Weckt den ersten Knoten (oder alle) in der FIFO-Warteschlange in „condition.await()“ auf.

Deadlock: Ein Phänomen, bei dem zwei oder mehr Prozesse aufgrund des Wettbewerbs um Ressourcen während der Ausführung aufeinander warten. Ohne äußere Kraft können sie nicht vorankommen.

So vermeiden Sie Deadlocks: Implementieren Sie die Ressourcensortierung, sodass die Reihenfolge der Sperren jedes Threads gleich bleibt.

 Sie sollten einen Test über die Verfahren für Ein- und Auszahlungen machen (dazu später mehr).

 Die Einzahlungsaufgabe benachrichtigt die Auszahlungsaufgabe, wenn sich der Kontostand ändert. Wenn die Auszahlungsaufgabe aktiviert wird, kann das Beurteilungsergebnis der Bedingung (Guthaben<Betrag) immer noch wahr sein. Wenn eine if-Anweisung verwendet wird, kann die Auszahlungsaufgabe zu falschen Auszahlungen führen.

Die Methode wait() sollte in einer Schleife und nicht in einer if-Anweisung verwendet werden. Nach der Änderung wird die Wartefunktion nur in Ausnahmesituationen aktiviert.

synchronized(object){
    while(!condition){
        try{
            object.wait();
        }
        catch(InterruptedException ex){
            // 处理异常
        }
    }
}

Pufferklasse 

 

Lese- und Schreibvorgänge in der Buffer-Klasse werden normalerweise in einer Single-Thread-Umgebung aufgerufen, um die Richtigkeit und Konsistenz der Daten sicherzustellen.

Wenn die Lesemethode aufgerufen wird und die Warteschlange leer ist, wartet die Lesemethode, bis neue Daten gelesen werden können.

Wenn die Schreibmethode aufgerufen wird und die Warteschlange voll ist, blockiert auch die Schreibmethode, bis in der Warteschlange genügend Platz zum Speichern neuer Daten vorhanden ist.

Blockierungswarteschlange 

(1) Blockierungswarteschlange: Eine Warteschlange, die zwei zusätzliche Vorgänge unterstützt, die blockierende Einfügungs- und Entfernungsmethoden unterstützen.

Blocking Queue ist eine threadsichere Warteschlangenzugriffsmethode

Wenn die blockierende Warteschlange Elemente einfügen muss und die Warteschlange voll ist, blockiert der Thread, bis die Warteschlange nicht mehr voll ist.

Wenn die blockierende Warteschlange Elemente löschen muss und die Warteschlange leer ist, blockiert der Thread, bis die Warteschlange nicht mehr leer ist.

(2) In Java unterstützte Blockierungswarteschlangen:

ArrayBlockingQueue

LinkedBlockingQueue

PriorityBlockingQueue

 Semaphor

创建:Semaphore semaphore = new Semaphore(3);

Holen Sie sich ein Semaphor: Semaphore.acquire();

Geben Sie ein Semaphor frei: Semaphore.release();

Eine Sperre kann nur eine gemeinsam genutzte Ressource sperren, und ein Semaphor kann mehrere gemeinsam genutzte Ressourcen sperren.

=>Semaphoren werden verwendet, um den Zugriff auf mehrere Ressourcen zu steuern, und Sperren werden verwendet, um den Zugriff auf eine Ressource zu steuern.

Sammlung synchronisieren

Synchronisierte Sammlungen: Thread-sichere Sammlungen implementieren.

ArrayList ist nicht synchronisiert. Lassen Sie synchronisierte Liste implementieren, um sie zu synchronisieren.

 Fork/Join

ist eine abstrakte Klasse

Fork/Join-Framework: Fork-Aufteilung und Join-Zusammenführung. Wird verwendet, um parallele Aufgaben auszuführen und große Probleme in Unterprobleme zu unterteilen.

Definition:

öffentliche abstrakte Klasse ForkJoinTask<V> implementiert Future<V>, Serializable{ }

V: Art des Ergebnisses der Aufgabenausführung

Future<V>: Gibt an, dass ForkJoinTask die Future-Schnittstelle implementiert, sodass sie zum Erhalten von Aufgabenausführungsergebnissen verwendet werden kann.

RecursiveAction (für Aufgaben, die keinen Wert zurückgeben) und RecursiveTask (für Aufgaben, die einen Wert zurückgeben) sind zwei Unterklassen von ForkJoinTask

Zum Spaß, ForkPool

RecursiveAction mainTask = new SortTask(list);
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(mainTask);

Ein Beispielcode, der eine Synchronisationssperre, Synchronized , zum Starten von 1000 Threads enthält und jeder Thread 1 zur Variablensumme mit einem Anfangswert von 0 hinzufügt.

import java.util.concurrent.*;
 
public class forFun {
    private Integer sum = new Integer(0);
    public static void main(String[] args) {
        forFun test = new forFun();
        System.out.println("What is sum ? " + test.sum);
    }
 
    public forFun() {
        synchronized(this){//同步代码块,确保只有一个线程可以访问以下代码
            ExecutorService executor = Executors.newFixedThreadPool(1000);//创建大小1000 的线程池
            for (int i = 0; i < 1000; i++) {
                executor.execute(new SumTask());//将SumTask交给线程池
            }
            executor.shutdown();
            while(!executor.isTerminated()) {//等待任务完成
            }
        }
    }
    class SumTask implements Runnable {// 定义一个实现Runnable接口的SumTask类
        public void run() {
            synchronized(this){// 同步代码块,确保只有一个线程可以访问以下代码
                int value = sum.intValue() + 1;
                sum = new Integer(value);
            }
        }
    }
}

Lasst uns Spaß haben, Einzahlungs-/Abhebungsaufgaben auf das Konto

Es gibt Thread-Pools und Synchronisationssperren. lz arbeitete früher mit seinem Mentor an der Blockchain und nutzte Solidity, um jeden Tag so etwas zu schreiben. Ich fühlte mich so nett und glücklich, als ich es sah.

import java.util.concurrent.*;

public class forFun {
    private static Account account = new Account();
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定大小为2的线程池
        executor.execute(new DepositTask());
        executor.execute(new WithdrawTask());
        executor.shutdown();
        System.out.println("Deposit Task\t\tWithdraw Task\t\tBalance");
    }

    // 存款任务
    public static class DepositTask implements Runnable {
        public void run() {
            try {
                while (true) {
                    account.deposit((int) (Math.random() * 10) + 1);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

    // 取款任务
    public static class WithdrawTask implements Runnable {
        public void run() {
            while (true) {
                account.withdraw((int) (Math.random() * 10) + 1);
            }
        }
    }

    // 账户类
    public static class Account {
        private int balance = 0;

        // 获取账户余额
        public int getBalance() {
            return balance;
        }

        // 存款方法,使用 synchronized 保证线程安全
        public synchronized void deposit(int amount) {
            balance += amount;
            System.out.println("Deposit " + amount +
                    "\t\t\t\t\t" + getBalance());
            notifyAll(); // 唤醒其他等待线程
        }

        // 取款方法,使用 synchronized 保证线程安全
        public synchronized void withdraw(int amount) {
            try {
                while (balance < amount)  // 当余额不足时,进入等待状态
                    wait();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }

            balance -= amount;
            System.out.println("\t\t\tWithdraw " + amount +
                    "\t\t" + getBalance());
        }
    }
}

Acho que você gosta

Origin blog.csdn.net/mynameispy/article/details/135306516
Recomendado
Clasificación