Heim Java JavaInterview Fragen Interviewfragen zu Java-Multithreading und Parallelität (1 bis 3 Fragen, mit Antworten)

Interviewfragen zu Java-Multithreading und Parallelität (1 bis 3 Fragen, mit Antworten)

Nov 23, 2019 pm 02:56 PM
java

Interviewfragen zu Java-Multithreading und Parallelität (1 bis 3 Fragen, mit Antworten)

1. DelayQueue verzögert unbegrenzte Blockierungswarteschlange

Wenn wir über die Verwendung und das Prinzip von DelayQueue sprechen, stellen wir zunächst DelayQueue vor. DelayQueue Ist eine unbegrenzte Blockierungswarteschlange Blockierungswarteschlange, aus der Elemente erst extrahiert werden können, wenn die Verzögerung abgelaufen ist. Der Kopf der Warteschlange ist das Delayed-Element, das nach Ablauf der Verzögerung am längsten gespeichert wird. (Empfohlene Studie: Java-Interview-Fragen)

DelayQueue-Blockierungswarteschlangen werden auch häufig in unserer Systementwicklung verwendet, zum Beispiel: Das Design des Cache-Systems, die Objekte im Cache, überschreiten die Leerlaufzeit muss aus dem Cache verschoben werden; das Aufgabenplanungssystem kann die Ausführungszeit der Aufgabe genau erfassen. Möglicherweise müssen wir viele zeitkritische Daten über Threads verarbeiten.

Wenn wir normale Threads verwenden, müssen wir alle Objekte einzeln durchlaufen, um festzustellen, ob die Daten abgelaufen sind. Erstens ist die Ausführungseffizienz nicht zu hoch Dies hat auch großen Einfluss auf die Genauigkeit der Daten. Eine Aufgabe, die um 12:00 Uhr ausgeführt werden muss, wird möglicherweise erst um 12:01 Uhr ausgeführt, was für Systeme mit hohen Datenanforderungen größere Nachteile mit sich bringt. Daraus können wir DelayQueue verwenden.

Im Folgenden finden Sie eine Einführung in DelayQueue und ein Beispiel. Und stellen Sie eine Implementierung der Delayed-Schnittstelle und Beispielcode bereit. DelayQueue ist eine BlockingQueue, deren spezialisierter Parameter Delayed ist.

(Studenten, die BlockingQueue nicht kennen, lernen bitte zuerst BlockingQueue kennen und lesen dann diesen Artikel) Der Vergleichsbenchmark ist der Verzögerungszeitwert der Implementierungsklasse der verzögerten Schnittstelle sollte ein fester Wert (endgültig) sein. DelayQueue wird intern mit PriorityQueue implementiert.

DelayQueue=BlockingQueue+PriorityQueue+Delayed
Nach dem Login kopieren

Die Schlüsselelemente von DelayQueue sind BlockingQueue, PriorityQueue und Delayed. Man kann sagen, dass DelayQueue eine BlockingQueue ist, die mithilfe einer Prioritätswarteschlange (PriorityQueue) implementiert wird. Der Vergleichsbasiswert der Prioritätswarteschlange ist die Zeit.

Ihre grundlegenden Definitionen lauten wie folgt

public interface Comparable<T> {
    public int compareTo(T o);
}
public interface Delayed extends Comparable<Delayed> {
    long getDelay(TimeUnit unit);
}
public class DelayQueue<E extends Delayed> implements BlockingQueue<E> {
    private final PriorityQueue<E> q = new PriorityQueue<E>();
}
Nach dem Login kopieren

Die interne Implementierung von DelayQueue verwendet eine Prioritätswarteschlange. Wenn die Angebotsmethode von DelayQueue aufgerufen wird, wird das Delayed-Objekt zur Prioritätswarteschlange q hinzugefügt. Wie folgt:

public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        E first = q.peek();
        q.offer(e);
        if (first == null || e.compareTo(first) < 0)
            available.signalAll();
        return true;
    } finally {
        lock.unlock();
    }
}
Nach dem Login kopieren

Die Take-Methode von DelayQueue entfernt den ersten der Prioritätswarteschlange q (Peek). Wenn der Verzögerungsschwellenwert nicht erreicht wird, wird eine Warteverarbeitung durchgeführt. Wie folgt:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        for (; ; ) {
            E first = q.peek();
            if (first == null) {
                available.await();
            } else {
                long delay = first.getDelay(TimeUnit.NANOSECONDS);
                if (delay > 0) {
                    long tl = available.awaitNanos(delay);
                } else {
                    E x = q.poll();
                    assert x != null;
                    if (q.size() != 0)
                        available.signalAll(); //wake up other takers return x;
                }
            }
        }
    } finally {
        lock.unlock();
    }
}
Nach dem Login kopieren

DelayQueue-Instanzanwendung

Ps: Um ein Aufrufverhalten zu haben, müssen die in DelayDeque gespeicherten Elemente die Delayed-Schnittstelle erben. Die Delayed-Schnittstelle macht das Objekt zu einem verzögerten Objekt, wodurch das in der DelayQueue-Klasse gespeicherte Objekt ein Aktivierungsdatum erhält. Diese Schnittstelle erzwingt die folgenden zwei Methoden.

Im Folgenden wird Verzögerung verwendet, um einen Cache zu implementieren. Es umfasst insgesamt drei Klassen: Pair, DelayItem, Cache

Pair-Klasse:

public class Pair<K, V> {
    public K first;
    public V second;
    public Pair() {
    }
    public Pair(K first, V second) {
        this.first = first;
        this.second = second;
    }
}
Nach dem Login kopieren

Das Folgende ist die Implementierung der Delay-Schnittstelle :

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class DelayItem<T> implements Delayed {
    /**
     * Base of nanosecond timings, to avoid wrapping
     */
    private static final long NANO_ORIGIN = System.nanoTime();
    /**
     * Returns nanosecond time offset by origin
     */
    final static long now() {
        return System.nanoTime() - NANO_ORIGIN;
    }
    /**
     * Sequence number to break scheduling ties, and in turn to guarantee FIFO order among tied
     * entries.
     */
    private static final AtomicLong sequencer = new AtomicLong(0);
    /**
     * Sequence number to break ties FIFO
     */
    private final long sequenceNumber;
    /**
     * The time the task is enabled to execute in nanoTime units
     */
    private final long time;
    private final T item;
    public DelayItem(T submit, long timeout) {
        this.time = now() + timeout;
        this.item = submit;
        this.sequenceNumber = sequencer.getAndIncrement();
    }
    public T getItem() {
        return this.item;
    }
    public long getDelay(TimeUnit unit) {
        long d = unit.convert(time - now(), TimeUnit.NANOSECONDS); return d;
    }
    public int compareTo(Delayed other) {
        if (other == this) // compare zero ONLY if same object return 0;
            if (other instanceof DelayItem) {
                DelayItem x = (DelayItem) other;
                long diff = time - x.time;
                if (diff < 0) return -1;
                else if (diff > 0) return 1;
                else if (sequenceNumber < x.sequenceNumber) return -1;
                else
                    return 1;
            }
        long d = (getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS));
        return (d == 0) ?0 :((d < 0) ?-1 :1);
    }
}
Nach dem Login kopieren

Das Folgende ist die Implementierung von Cache, einschließlich Put- und Get-Methoden

import javafx.util.Pair;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Cache<K, V> {
    private static final Logger LOG = Logger.getLogger(Cache.class.getName());
    private ConcurrentMap<K, V> cacheObjMap = new ConcurrentHashMap<K, V>();
    private DelayQueue<DelayItem<Pair<K, V>>> q = new DelayQueue<DelayItem<Pair<K, V>>>();
    private Thread daemonThread;
    public Cache() {
        Runnable daemonTask = new Runnable() {
            public void run() {
                daemonCheck();
            }
        };
        daemonThread = new Thread(daemonTask);
        daemonThread.setDaemon(true);
        daemonThread.setName("Cache Daemon");
        daemonThread.start();
    }
    private void daemonCheck() {
        if (LOG.isLoggable(Level.INFO)) LOG.info("cache service started.");
        for (; ; ) {
            try {
                DelayItem<Pair<K, V>> delayItem = q.take();
                if (delayItem != null) {
                    // 超时对象处理
                    Pair<K, V> pair = delayItem.getItem();
                    cacheObjMap.remove(pair.first, pair.second); // compare and remove
                }
            } catch (InterruptedException e) {
                if (LOG.isLoggable(Level.SEVERE)) LOG.log(Level.SEVERE, e.getMessage(), e);
                break;
            }
        }
        if (LOG.isLoggable(Level.INFO)) LOG.info("cache service stopped.");
    }
    // 添加缓存对象
    public void put(K key, V value, long time, TimeUnit unit) {
        V oldValue = cacheObjMap.put(key, value);
        if (oldValue != null) q.remove(key);
        long nanoTime = TimeUnit.NANOSECONDS.convert(time, unit);
        q.put(new DelayItem<Pair<K, V>>(new Pair<K, V>(key, value), nanoTime));
    }
    public V get(K key) {
        return cacheObjMap.get(key);
    }
}
Nach dem Login kopieren

Testen Sie die Hauptmethode:

// 测试入口函数
public static void main(String[] args) throws Exception {
    Cache<Integer, String> cache = new Cache<Integer, String>();
    cache.put(1, "aaaa", 3, TimeUnit.SECONDS);
    Thread.sleep(1000 * 2);
    {
        String str = cache.get(1);
        System.out.println(str);
    }
    Thread.sleep(1000 * 2);
    {
        String str = cache.get(1);
        System.out.println(str);
    }
}
Nach dem Login kopieren

Das Ausgabeergebnis ist:

aaaa
null
Nach dem Login kopieren

Wir sehen das obige Ergebnis. Wenn die Verzögerungszeit überschritten wird, gehen die Daten im Cache automatisch verloren und das Ergebnis wird gelöscht null sein.

2. Gleichzeitige (Sammel-)Warteschlange – nicht blockierende Warteschlange

Nicht blockierende Warteschlange

Zuerst müssen wir Verstehen Sie es einfach Als Nächstes erfahren Sie, was eine nicht blockierende Warteschlange ist:

Im Gegensatz zur blockierenden Warteschlange wird die Ausführung der nicht blockierenden Warteschlange nicht blockiert, unabhängig davon, ob es sich um das Entfernen von Verbrauchern oder den Beitritt von Produzenten handelt . Unter der Haube nutzen nicht blockierende Warteschlangen CAS (Compare and Swap), um eine nicht blockierende Thread-Ausführung zu erreichen.

Einfache Vorgänge für nicht blockierende Warteschlangen

Wie bei blockierenden Warteschlangen sind übliche Methoden in nicht blockierenden Warteschlangen das Entfernen und Einreihen in die Warteschlange.

offer(): Eine von der Warteschlangenschnittstelle geerbte Methode, die den Warteschlangeneintragsvorgang implementiert und die Ausführung des Threads nicht behindert, gibt sie true zurück;

poll(): Verschieben Sie den Kopfknotenzeiger, geben Sie das Kopfknotenelement zurück und entfernen Sie das Kopfknotenelement aus der Warteschlange. Geben Sie null zurück , geben Sie das Kopfknotenelement zurück. Wenn die Warteschlange leer ist, wird null zurückgegeben

3. Nicht blockierender Algorithmus

Zuerst müssen wir pessimistisches Sperren und optimistisches Sperren verstehen

Pessimistisches Sperren: Angenommen, die Parallelitätsumgebung ist pessimistisch, die Konsistenz wird zerstört, daher müssen Konflikte vollständig sein durch Exklusivsperren verboten. Es gibt eine klassische Metapher: „Wenn du die Tür nicht abschließt, bricht der Unruhestifter ein und macht Chaos.“ Also „man kann immer nur eine Person auf einmal öffnen und hereinlassen, damit man einen behalten kann.“ ein Auge auf ihn haben.

Optimistisches Sperren: Es wird davon ausgegangen, dass die Parallelitätsumgebung optimistisch ist, das heißt, obwohl es Parallelitätskonflikte gibt, können die Konflikte entdeckt werden und verursachen keinen Schaden Nachdem die Parallelitätskonflikte festgestellt wurden, erfolgt ein Neustart oder ein erneuter Versuch. Eine Analogie lautet: „Wenn Sie die Tür nicht abschließen, werden die Unruhestifter zwar einbrechen, aber sie werden wissen, ob sie Sie zerstören wollen.“ Daher können Sie einfach alle hereinlassen und warten, bis Sie herausfinden, dass sie es tun zerstören wollen. „Eine Entscheidung treffen“.

Es wird allgemein angenommen, dass die Leistung des optimistischen Sperrens höher ist als die des pessimistischen Sperrens, insbesondere in einigen komplexen Szenarien. Dies liegt hauptsächlich daran, dass pessimistische Sperren auch bestimmte Vorgänge schützen, die beim Sperren keinen Schaden anrichten. Während der Wettbewerb um optimistische Sperren nur bei kleinsten Parallelitätskonflikten auftritt, ist die Granularität der pessimistischen Sperren am geringsten “. Der Entwurf optimistischer Sperren ist jedoch häufig komplexer, sodass in komplexen Szenarien häufig pessimistische Sperren verwendet werden. Stellen Sie zunächst die Korrektheit sicher und verfolgen Sie dann gegebenenfalls die Leistung.

Die Implementierung des optimistischen Sperrens erfordert häufig Hardwareunterstützung. Die meisten Prozessoren implementieren eine CAS-Anweisung, um die Semantik von „Compare And Swap“ zu implementieren (Swap bedeutet hier „Swap in“, also festlegen). Bildung eines grundlegenden optimistischen Schlosses. CAS enthält 3 Operanden:

Der zu lesende und zu schreibende Speicherort V

Der zu vergleichende Wert A

Der neue zu schreibende Wert B

CAS aktualisiert den Wert von Position V genau dann atomar mit dem neuen Wert B, wenn der Wert von Position V gleich A ist; andernfalls wird keine Operation ausgeführt. Unabhängig davon, ob der Wert der Position V gleich A ist, wird der ursprüngliche Wert von V zurückgegeben. Eine interessante Tatsache ist, dass „die Verwendung von CAS zur Steuerung der Parallelität“ nicht gleichbedeutend mit der „Verwendung optimistischer Sperren“ ist. CAS ist lediglich ein Mittel, um sowohl optimistisches als auch pessimistisches Sperren zu erreichen. Optimismus und Pessimismus sind lediglich Strategien zur Parallelitätskontrolle.

Das obige ist der detaillierte Inhalt vonInterviewfragen zu Java-Multithreading und Parallelität (1 bis 3 Fragen, mit Antworten). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Wie man alles in Myrise freischaltet
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Quadratwurzel in Java Quadratwurzel in Java Aug 30, 2024 pm 04:26 PM

Leitfaden zur Quadratwurzel in Java. Hier diskutieren wir anhand eines Beispiels und seiner Code-Implementierung, wie Quadratwurzel in Java funktioniert.

Perfekte Zahl in Java Perfekte Zahl in Java Aug 30, 2024 pm 04:28 PM

Leitfaden zur perfekten Zahl in Java. Hier besprechen wir die Definition, Wie prüft man die perfekte Zahl in Java?, Beispiele mit Code-Implementierung.

Armstrong-Zahl in Java Armstrong-Zahl in Java Aug 30, 2024 pm 04:26 PM

Leitfaden zur Armstrong-Zahl in Java. Hier besprechen wir eine Einführung in die Armstrong-Zahl in Java zusammen mit einem Teil des Codes.

Zufallszahlengenerator in Java Zufallszahlengenerator in Java Aug 30, 2024 pm 04:27 PM

Leitfaden zum Zufallszahlengenerator in Java. Hier besprechen wir Funktionen in Java anhand von Beispielen und zwei verschiedene Generatoren anhand ihrer Beispiele.

Weka in Java Weka in Java Aug 30, 2024 pm 04:28 PM

Leitfaden für Weka in Java. Hier besprechen wir die Einführung, die Verwendung von Weka Java, die Art der Plattform und die Vorteile anhand von Beispielen.

Smith-Nummer in Java Smith-Nummer in Java Aug 30, 2024 pm 04:28 PM

Leitfaden zur Smith-Zahl in Java. Hier besprechen wir die Definition: Wie überprüft man die Smith-Nummer in Java? Beispiel mit Code-Implementierung.

Fragen zum Java Spring-Interview Fragen zum Java Spring-Interview Aug 30, 2024 pm 04:29 PM

In diesem Artikel haben wir die am häufigsten gestellten Fragen zu Java Spring-Interviews mit ihren detaillierten Antworten zusammengestellt. Damit Sie das Interview knacken können.

Brechen oder aus Java 8 Stream foreach zurückkehren? Brechen oder aus Java 8 Stream foreach zurückkehren? Feb 07, 2025 pm 12:09 PM

Java 8 führt die Stream -API ein und bietet eine leistungsstarke und ausdrucksstarke Möglichkeit, Datensammlungen zu verarbeiten. Eine häufige Frage bei der Verwendung von Stream lautet jedoch: Wie kann man von einem Foreach -Betrieb brechen oder zurückkehren? Herkömmliche Schleifen ermöglichen eine frühzeitige Unterbrechung oder Rückkehr, aber die Stream's foreach -Methode unterstützt diese Methode nicht direkt. In diesem Artikel werden die Gründe erläutert und alternative Methoden zur Implementierung vorzeitiger Beendigung in Strahlverarbeitungssystemen erforscht. Weitere Lektüre: Java Stream API -Verbesserungen Stream foreach verstehen Die Foreach -Methode ist ein Terminalbetrieb, der einen Vorgang für jedes Element im Stream ausführt. Seine Designabsicht ist

See all articles