On blocking queue
Posted by Bret on Sat, 16 Oct 2021 08:51:14 +0200
1. What is a blocking queue?
- The first is a queue. The second is blocking. In the producer consumer model, the producer produces while the consumer consumes, so imagine these two threads as two threads, one is plugging in and the other is taking out.
- When the production thread is full and the consumption thread is not taken out in time, it is blocked.
- When the consumer thread consumes too fast, the producer has not had time to produce, resulting in nothing to take and blocking.
- Benefits of blocking queues: you don't need to care when to block threads and wake up threads.
2. Interface and its implementation class
- ArrayBlockQueue: a bounded blocking queue composed of an array structure.
- LinkedBlockingQueue: a bounded (but the default size is Integer.MAX_VALUE) blocking queue composed of a linked list structure. Although bounded, it is equivalent to unbounded.
- Synchronous queue: a blocking queue that does not store elements, that is, a queue of individual elements.
- LinkedBlockingDeque: a bidirectional blocking queue composed of a linked list structure.
- LinkedTransferQueue: an unbounded blocking queue composed of a linked list structure.
- DelayQueue: delay unbounded blocking queue implemented using priority queue.
- PriorityBlockQueue: an unbounded blocking queue that supports prioritization.
Note: really master the first three.
3. Core method
Throw exception | When the blocking queue is full: IIIegalStateException: Queue full will be thrown when add ing an element to the queue. When the blocking queue is empty: NoSuchException will be thrown when removing an element from the queue. remove() will be used according to the first in first out principle. If remove(Object o) is used, the specific element will be deleted |
---|
specific characteristics | Insert method, success true, failure false, remove method: successfully return the queue element, and return empty if the queue is not |
Always blocked | When the blocking queue is full, the producer continues to put elements into the queue, and the queue will block the production thread until the put data or response interrupt exits. When the blocking queue is empty, the consumer thread attempts to take elements from the queue, and the queue will block the consumer thread until the queue is available. |
Timeout exit | When the blocking queue is full, the producer thread in the queue will be blocked for a certain time. After the time limit is exceeded, the producer thread will exit |
4. Producers and consumers
- Tradition:
class ShareData {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws Exception{
// Sync code block, lock
lock.lock();
try {
// Judge that while must be used here. If if if is used, there will be a false wake-up problem
while(number != 0) {
// Waiting cannot produce
condition.await();
}
// work
number++;
System.out.println(Thread.currentThread().getName() + "\t " + number);
// Notification wake up
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws Exception{
// Sync code block, lock
lock.lock();
try {
// judge
while(number == 0) {
// Waiting cannot be consumed
condition.await();
}
// work
number--;
System.out.println(Thread.currentThread().getName() + "\t " + number);
// Notification wake up
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ProdConsumerTraditionDemo {
public static void main(String[] args) {
// High cohesion and low coupling cohesion refer to an air conditioner with its own method to adjust the temperature
ShareData shareData = new ShareData();
// t1 thread, production
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
shareData.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
}, "t1").start();
// t2 thread, consumption
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
shareData.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
}, "t2").start();
}
}
Problem: too many details need to be controlled. - Producers and consumers using blocking queues:
Knowledge points: volatile, CAS, atomic class, blocking queue, thread interaction, atomic referenceclass MyResource {
// It is enabled by default for production and consumption
// volatile is used here to maintain the visibility of data, that is, when FLAG is modified, other threads should be notified to modify it immediately
private volatile boolean FLAG = true;
// Use atomic wrapper classes instead of number++
private AtomicInteger atomicInteger = new AtomicInteger();
// You cannot instantiate a specific SynchronousBlockingQueue to satisfy the condition
BlockingQueue<String> blockingQueue = null;
// Instead, you should use the structure injection method in dependency injection to pass in
public MyResource(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
// Find out what the incoming class is
System.out.println(blockingQueue.getClass().getName());
}
/**
* production
* @throws Exception
*/
public void myProd() throws Exception{
String data = null;
boolean retValue;
// When judging a multithreaded environment, you must use while to prevent false wakeups
// When FLAG is true, start production
while(FLAG) {
data = atomicInteger.incrementAndGet() + "";
// Store 1 data in 2 seconds
retValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
if(retValue) {
System.out.println(Thread.currentThread().getName() + "\t Insert queue:" + data + "success" );
} else {
System.out.println(Thread.currentThread().getName() + "\t Insert queue:" + data + "fail" );
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "\t Stop production, indicating FLAG=false,Production introduction");
}
/**
* consumption
* @throws Exception
*/
public void myConsumer() throws Exception{
String retValue;
// When judging a multithreaded environment, you must use while to prevent false wakeups
// When FLAG is true, start production
while(FLAG) {
// Store 1 data in 2 seconds
retValue = blockingQueue.poll(2L, TimeUnit.SECONDS);
if(retValue != null && retValue != "") {
System.out.println(Thread.currentThread().getName() + "\t Consumption queue:" + retValue + "success" );
} else {
FLAG = false;
System.out.println(Thread.currentThread().getName() + "\t Consumption failed. The queue is empty. Exit" );
// Exit the consumption queue
return;
}
}
}
/**
* Judgment of stopping production
*/
public void stop() {
this.FLAG = false;
}
}
public class ProdConsumerBlockingQueueDemo {
public static void main(String[] args) {
// Pass in the specific implementation class, ArrayBlockingQueue
MyResource myResource = new MyResource(new ArrayBlockingQueue<String>(10));
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t Production thread start");
System.out.println("");
System.out.println("");
try {
myResource.myProd();
System.out.println("");
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
}, "prod").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t Consumer thread start");
try {
myResource.myConsumer();
} catch (Exception e) {
e.printStackTrace();
}
}, "consumer").start();
// After 5 seconds, stop production and consumption
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("");
System.out.println("");
System.out.println("5 Seconds later, the production and consumption threads stop and the thread ends");
myResource.stop();
}
}
Topics:
Java
RabbitMQ
data structure