Technologieaustausch

[Betriebssystem] Blockierungswarteschlange und Producer-Consumer-Modell

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Blockierungswarteschlange

1. Konzept

Bei der Sperrwarteschlange handelt es sich um eine besondere Art von Warteschlange. Sie folgt ebenfalls dem Prinzip „First in, first out“.

Die Blockierungswarteschlange kann eine threadsichere Datenstruktur sein und weist die folgenden Eigenschaften auf:

  • Wenn die Warteschlange voll ist, wird der weitere Eintritt in die Warteschlange blockiert, bis andere Threads Elemente aus der Warteschlange übernehmen..
  • Wenn die Warteschlange leer ist, wird auch das weitere Entfernen aus der Warteschlange blockiert, bis andere Threads Elemente in die Warteschlange einfügen.

Ein typisches Anwendungsszenario für Blockierungswarteschlangen ist das „Produzenten-Verbraucher-Modell“. Dies ist ein sehr typisches Entwicklungsmodell
Hersteller

Das Verbrauchermodell löst das starke Kopplungsproblem zwischen Produzenten und Verbrauchern durch einen Container.

Produzenten und Konsumenten kommunizieren nicht direkt miteinander, sondern über blockierende Warteschlangen. Nachdem der Produzent die Daten erzeugt hat, muss er daher nicht darauf warten, dass der Konsument sie verarbeitet, sondern wirft sie direkt in die blockierende Warteschlange fragt den Produzenten nicht nach Daten, sondern wird direkt aus der Blockierungswarteschlange entnommen.

Bei der Umsetzung des Produzenten-Konsumenten-Modells gibt es zwei Hauptschritte:

  1. Gerätehersteller
  2. Verbraucher realisieren

2. Blockierungswarteschlange in der Standardbibliothek

In der Java-Standardbibliothek ist eine Blockierungswarteschlange integriert. Wenn wir in einigen Programmen Blockierungswarteschlangen verwenden müssen, können wir die Blockierungswarteschlange direkt in der Standardbibliothek verwenden.
Dürfen.

  • BlockingQueue ist eine Schnittstelle. Die eigentliche Implementierungsklasse ist LinkedBlockingQueue
  • Die Put-Methode wird zum Blockieren des Warteschlangeneintritts und die Take-Methode zum Blockieren des Warteschlangenausgangs verwendet.
  • BlockingQueue verfügt auch über Methoden wie Offer, Poll und Peek, diese Methoden verfügen jedoch nicht über blockierende Eigenschaften.

Der Pseudocode für die Blockierungswarteschlange lautet wie folgt:

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

// ⼊队列
queue.put("abc");

// 出队列. 如果没有 put 直接 take, 就会阻塞.
String elem = queue.take();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3. Produzenten-Konsumenten-Modell

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

	BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>();
	
	Thread customer = new Thread(() -> {
		while (true) {
			try {
				int value = blockingQueue.take();
				System.out.println("消费元素: " + value);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}, "消费者");
	
customer.start();

	Thread producer = new Thread(() -> {
		Random random = new Random();
		while (true) {
			try {
				int num = random.nextInt(1000);
				System.out.println("生产元素: " + num);
				blockingQueue.put(num);
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}, "生产者");
	
	producer.start();
	customer.join();
	producer.join();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

4. Implementierung der Blockierungswarteschlange

  1. Dies wird durch die „Circular Queue“-Methode erreicht.
  2. Zur Verriegelungssteuerung synchronisiert verwenden.
  3. Wenn put ein Element einfügt, ermittelt es, ob die Warteschlange voll ist und wartet (Beachten Sie, dass das Warten in einer Schleife ausgeführt werden muss. Wenn es aktiviert wird, ist die Warteschlange möglicherweise nicht voll, da möglicherweise mehrere Threads gleichzeitig aktiviert werden.) .
  4. Wenn take das Element herausnimmt, stellt es fest, ob die Warteschlange leer ist und wartet (es ist auch eine Warteschleife).
public class BlockingQueue {

	private int[] items = new int[1000];
	private volatile int size = 0;
	private volatile int head = 0;
	private volatile int tail = 0;
	
	public void put(int value) throws InterruptedException {
		synchronized (this) {
		// 此处最好使⽤ while.
		// 否则 notifyAll 的时候, 该线程从 wait 中被唤醒,
		// 但是紧接着并未抢占到锁. 当锁被抢占的时候, 可能⼜已经队列满了
		// 就只能继续等待
			while (size == items.length) {
				wait();
			}
			items[tail] = value;
			tail = (tail + 1) % items.length;
			size++;
			notifyAll();
		}
	}
	
	public int take() throws InterruptedException {
		int ret = 0;
		synchronized (this) {
			while (size == 0) {
				wait();
			}
			ret = items[head];
			head = (head + 1) % items.length;
			size--;
			notifyAll();
		}
		return ret;
	}
	
	public synchronized int size() {
		return size;
	}
	
	// 测试代码
	public static void main(String[] args) throws InterruptedException {
		BlockingQueue blockingQueue = new BlockingQueue();
		Thread customer = new Thread(() -> {
		while (true) {
			try {
				int value = blockingQueue.take();
				System.out.println(value);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}, "消费者");	
	
		customer.start();
		Thread producer = new Thread(() -> {
			Random random = new Random();
				while (true) {					try {
						blockingQueue.put(random.nextInt(10000));
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}, "生产者");
			
		producer.start();
		customer.join();
		producer.join();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

Zusammenfassen

  1. Die Blockierungswarteschlange entspricht einem Puffer, der die Verarbeitungsfähigkeiten von Produzenten und Konsumenten ausgleicht (Peak-Clipping und -Füllung).
  2. Blockierungswarteschlangen können auch Produzenten und Konsumenten entkoppeln.