Rumah >Java >javaTutorial >Menganalisis contoh menyekat baris gilir di Jawa
Barisan gilir yang menyekat ialah baris gilir khas Seperti baris gilir biasa dalam struktur data, ia juga mengikut masuk pertama , keluar dahulu Pada masa yang sama, baris gilir menyekat ialah struktur data yang boleh memastikan keselamatan benang dan mempunyai dua ciri berikut: apabila baris gilir penuh, terus memasukkan elemen ke dalam baris gilir akan menyebabkan baris gilir disekat sehingga utas lain ambilnya dari baris gilir. Alih keluar elemen; apabila baris gilir kosong, meneruskan dequeue juga akan menyekat baris gilir sehingga benang lain memasukkan elemen ke dalam baris gilir
Tambahan: Penyekatan benang bermakna kod tidak akan dilaksanakan pada kali ini, iaitu Sistem pengendalian tidak akan menjadualkan utas ini kepada CPU untuk dilaksanakan pada masa ini
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.BlockingDeque; public class Test { public static void main(String[] args) throws InterruptedException { //不能直接newBlockingDeque,因为它是一个接口,要向上转型 //LinkedBlockingDeque内部是基于链表方式来实现的 BlockingDeque<String> queue=new LinkedBlockingDeque<>(10);//此处可以指定一个具体的数字,这里的的10代表队列的最大容量 queue.put("hello"); String elem=queue.take(); System.out.println(elem); elem=queue.take(); System.out.println(elem); } }
Nota: Kaedah put mempunyai fungsi menyekat , tetapi tawaran tidak. Oleh itu, kaedah put biasanya digunakan (sebab kaedah tawaran boleh digunakan ialah BlockingDeque
mewarisi Queue
)
Hasil cetakan adalah seperti yang ditunjukkan di atas, apabila hello dicetak Selepas itu, baris gilir kosong, dan kod tidak akan terus dilaksanakan sehingga elem=queue.take();
Pada masa ini, benang memasuki keadaan menunggu yang menyekat, dan tiada apa-apa akan dicetak sehingga utas lain meletakkan elemen baharu ke dalam baris gilir
Model pengeluar-pengguna ialah kaedah pengaturcaraan yang biasa digunakan dalam pembangunan pelayan dan pembangunan bahagian belakang. Ia biasanya digunakan untuk penyahgandingan dan pencukuran puncak dan pengisian lembah.
Gandingan tinggi: korelasi antara dua modul kod adalah agak tinggi
Kepaduan tinggi: elemen dalam modul kod disepadukan rapat antara satu sama lain
Oleh itu, kami biasanya mengejar kohesi tinggi dan gandingan rendah. Ini akan mempercepatkan kecekapan pelaksanaan, dan model pengeluar-pengguna boleh digunakan untuk memisahkan
Mari kita ambil situasi dalam kehidupan sebenar sebagai contoh dua Pelayan: Pelayan A dan pelayan B Apabila pelayan A menghantar data kepada B, jika ia dihantar secara langsung, maka sama ada A akan menolak data ke B, atau B akan menarik data dari A. Kedua-duanya memerlukan A dan B untuk berinteraksi secara langsung, jadi. Terdapat hubungan pergantungan antara A dan B (darjah gandingan antara A dan B agak tinggi). Jika pelayan perlu dikembangkan pada masa hadapan, seperti menambah pelayan C untuk membenarkan A menghantar data ke C, maka perubahan akan menjadi lebih rumit dan kecekapan akan dikurangkan. Pada masa ini, kita boleh menambah baris gilir, yang merupakan baris gilir menyekat Jika A menulis data ke baris gilir dan B mengambilnya daripadanya, maka baris gilir adalah bersamaan dengan stesen pemindahan (atau tempat perdagangan), dan A adalah bersamaan dengan. pengeluar (menyediakan data). B adalah setara dengan pengguna (menerima data Pada masa ini, model pengeluar-pengguna dibentuk, yang akan menjadikan kod kurang digabungkan, lebih mudah untuk diselenggara, dan lebih cekap dalam pelaksanaan).
Dalam komputer, pengeluar bertindak sebagai satu kumpulan benang, dan pengguna bertindak sebagai kumpulan benang lain dan tempat perdagangan boleh menggunakan baris gilir menyekat
Dalam kehidupan sebenar
Empangan adalah komponen yang sangat penting dalam sungai Jika tiada empangan, mari kita bayangkan hasil: apabila tiba musim banjir, apabila banyak air di hulu, jumlah air yang banyak akan mencurah ke bahagian hilir, menyebabkan banjir dan membanjiri tanaman manakala pada musim kemarau, akan sangat sedikit air di bahagian bawah, yang boleh menyebabkan kemarau. Sekiranya terdapat empangan, empangan akan menyimpan lebihan air di dalam empangan semasa musim banjir, menutup pintu pagar untuk menyimpan air, dan membenarkan air hulu mengalir ke hilir pada kadar tertentu untuk mengelakkan gelombang hujan lebat secara tiba-tiba daripada membanjiri hilir, supaya hilir tidak banjir. Semasa musim kemarau, empangan mengeluarkan air yang disimpan sebelum ini dan membenarkan air mengalir ke hilir pada kadar tertentu untuk mengelakkan hiliran daripada kekurangan air Ini boleh mengelakkan kedua-dua banjir semasa musim banjir dan kemarau semasa musim kering.
Puncak: bersamaan dengan musim banjir
Lembah: bersamaan dengan tempoh kering
Dalam komputer
Keadaan ini juga sangat tipikal dalam komputer, terutamanya dalam pembangunan pelayan biasanya Permintaan dimajukan kepada perniagaan pelayan, seperti beberapa pelayan produk, pelayan pengguna, pelayan pedagang (yang menyimpan maklumat pedagang), dan pelayan siaran langsung. Walau bagaimanapun, kerana bilangan permintaan daripada Internet tidak dapat dikawal, ia adalah bersamaan dengan air hulu Jika gelombang besar permintaan tiba-tiba datang, walaupun pintu masuk boleh mengendalikannya, banyak pelayan berikutnya akan runtuh selepas menerima banyak permintaan (memproses satu. permintaan melibatkan beberapa siri operasi pangkalan data, kerana kecekapan operasi berkaitan pangkalan data agak rendah Jika terdapat terlalu banyak permintaan, ia tidak boleh diproses, jadi ia akan ranap)
所以实际情况中网关和业务服务器之间往往用一个队列来缓冲,这个队列就是阻塞队列(交易场所),用这个队列来实现生产者(网关)消费者(业务服务器)模型,把请求缓存到队列中,后面的消费者(业务服务器)按照自己固定的速率去读请求。这样当请求很多时,虽然队列服务器可能会稍微受到一定压力,但能保证业务服务器的安全。
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class TestDemo { public static void main(String[] args) { // 使用一个 BlockingQueue 作为交易场所 BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(); // 此线程作为消费者 Thread customer = new Thread() { @Override public void run() { while (true) { // 取队首元素 try { Integer value = queue.take(); System.out.println("消费元素: " + value); } catch (InterruptedException e) { e.printStackTrace(); } } } }; customer.start(); // 此线程作为生产者 Thread producer = new Thread() { @Override public void run() { for (int i = 1; i <= 10000; i++) { System.out.println("生产了元素: " + i); try { queue.put(i); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; producer.start(); try { customer.join(); producer.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
打印如上(此代码是让生产者通过sleep每过1秒生产一个元素,而消费者不使用sleep,所以每当生产一个元素时,消费者都会立马消费一个元素)
在学会如何使用BlockingQueue
后,那么如何自己去实现一个呢?
主要思路:
1.利用数组
2.head代表队头,tail代表队尾
3.head和tail重合后到底是空的还是满的判断方法:专门定义一个size记录当前队列元素个数,入队列时size加1出队列时size减1,当size为0表示空,为数组最大长度就是满的(也可以浪费一个数组空间用head和tail重合表示空,用tail+1和head重合表示满,但此方法较为麻烦,上一个方法较为直观,因此我们使用上一个方法)
public class Test2 { static class BlockingQueue { private int[] items = new int[1000]; // 此处的1000相当于队列的最大容量, 此处暂时不考虑扩容的问题. private int head = 0;//定义队头 private int tail = 0;//定义队尾 private int size = 0;//数组大小 private Object locker = new Object(); // put 用来入队列 public void put(int item) throws InterruptedException { synchronized (locker) { while (size == items.length) { // 队列已经满了,阻塞队列开始阻塞 locker.wait(); } items[tail] = item; tail++; // 如果到达末尾, 就回到起始位置. if (tail >= items.length) { tail = 0; } size++; locker.notify(); } } // take 用来出队列 public int take() throws InterruptedException { int ret = 0; synchronized (locker) { while (size == 0) { // 对于阻塞队列来说, 如果队列为空, 再尝试取元素, 就要阻塞 locker.wait(); } ret = items[head]; head++; if (head >= items.length) { head = 0; } size--; // 此处的notify 用来唤醒 put 中的 wait locker.notify(); } return ret; } } public static void main(String[] args) throws InterruptedException { BlockingQueue queue = new BlockingQueue(); // 消费者线程 Thread consumer = new Thread() { @Override public void run() { while (true) { try { int elem = queue.take(); System.out.println("消费元素: " + elem); } catch (InterruptedException e) { e.printStackTrace(); } } } }; consumer.start(); // 生产者线程 Thread producer = new Thread() { @Override public void run() { for (int i = 1; i < 10000; i++) { System.out.println("生产元素: " + i); try { queue.put(i); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; producer.start(); consumer.join(); producer.join(); } }
运行结果如上。
注意:
1.wait和notify的正确使用
2.put和take都会产生阻塞情况,但阻塞条件是对立的,wait不会同时触发(put唤醒take阻塞,take唤醒put阻塞)
Atas ialah kandungan terperinci Menganalisis contoh menyekat baris gilir di Jawa. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!