Berbagi teknologi

[Sistem operasi] Memblokir antrian dan model produsen-konsumen

2024-07-12

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

memblokir antrian

1. Konsep

Antrian pemblokiran adalah jenis antrian khusus. Antrian ini juga mengikuti prinsip "masuk pertama, keluar pertama".

Antrian pemblokiran dapat berupa struktur data yang aman untuk thread dan memiliki karakteristik berikut:

  • Ketika antrian sudah penuh, terus masuk ke antrian akan diblokir sampai thread lain mengambil elemen dari antrian..
  • Ketika antrian kosong, melanjutkan ke dequeue juga akan memblokir sampai thread lain memasukkan elemen ke dalam antrian.

Skenario penerapan pemblokiran antrian yang umum adalah "model produsen-konsumen". Ini adalah model pengembangan yang sangat umum
produsen

Model konsumen memecahkan masalah ikatan yang kuat antara produsen dan konsumen melalui sebuah wadah.

Produsen dan konsumen tidak saling berkomunikasi secara langsung, melainkan berkomunikasi melalui antrian pemblokiran. Oleh karena itu, setelah produsen menghasilkan data, tidak perlu menunggu konsumen memprosesnya, melainkan langsung membuangnya ke antrian pemblokiran tidak meminta data kepada produser, melainkan diambil langsung dari antrean pemblokiran.

Ada dua langkah utama dalam penerapan model produsen-konsumen:

  1. produser pelaksana
  2. menyadari konsumen

2. Memblokir antrian di perpustakaan standar

Ada antrian pemblokiran yang dibangun di perpustakaan standar Java. Jika kita perlu menggunakan antrian pemblokiran di beberapa program, kita bisa langsung menggunakan antrian pemblokiran di perpustakaan standar.
Bisa.

  • BlockingQueue adalah sebuah antarmuka. Kelas implementasi sebenarnya adalah LinkedBlockingQueue
  • Metode put digunakan untuk memblokir masuknya antrian, dan metode take digunakan untuk memblokir keluar antrian.
  • BlockingQueue juga memiliki metode seperti penawaran, polling, dan mengintip, tetapi metode ini tidak memiliki karakteristik pemblokiran.

Kode semu untuk memblokir antrian adalah sebagai berikut:

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

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

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

3. Model produsen-konsumen

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. Memblokir implementasi antrian

  1. Hal ini dicapai melalui metode "antrian melingkar".
  2. Gunakan tersinkronisasi untuk mengunci kontrol.
  3. Ketika put memasukkan sebuah elemen, ia menentukan apakah antrian sudah penuh dan menunggu. .
  4. Ketika take mengeluarkan elemen, ia menentukan apakah antriannya kosong dan menunggu (Ini juga merupakan loop tunggu)
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

Meringkaskan

  1. Antrian pemblokiran setara dengan buffer, menyeimbangkan kemampuan pemrosesan produsen dan konsumen (Puncak kliping dan pengisian).
  2. Memblokir antrian juga dapat memisahkan produsen dan konsumen.