5 Ways Java multithreading model of production and consumption (not fully understood)

Reference blog:

  1. 5 Ways Java implementation of production and consumption models: https://blog.csdn.net/zhaohong_bo/article/details/89378488
  2. Concurrent Tools (C) controls the number of concurrent threads of Semaphore: http://ifeve.com/concurrency-semaphore/

@ [TOC] (Foreword) producer and consumer problem is a classic problem of threading model: producers and consumers share the same memory space in the same period, the producer added to the storage space products, from consumer storage Remove the product space, storage space when empty, consumers obstruction, when storage space is full, the producer blocked.

Here Insert Picture Description
The following solution, in fact, are essentially implements a blocking queue. Is empty, the consumer blocking, full, the producer blocked.

1. Use wait () and notify () implemented

This is the simplest and most basic implementation, the buffer is full and empty calls the wait () method to wait, when producers to produce a product or a product after consumer spending will wake up all threads.

/*
	 * 1.使用wait()和notify()实现
	 */
	public static void testProductConsumeByWaitAndNotify() {
		final int size = 10;
		final Queue<String> queue = new ArrayDeque<String>(size);
		final Object lock = new Object();

		Runnable producer = new Runnable() {

			public void run() {
				for (int i = 0; i < 30; i++) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					String msg = "消息:" + i;
					// 队列未满,一直往里放消息
					synchronized (lock) {
						while (size == queue.size()) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						queue.offer(msg);
						lock.notifyAll();
					}

					System.out.println(msg + " 已发送");
				}
			}
		};

		Runnable consumer = new Runnable() {

			public void run() {
				while (true) {
					try {
						Thread.sleep(200);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}

					synchronized (lock) {
						while (queue.size() == 0) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						String msg = queue.poll();
						System.out.println(msg + "已消费");
						lock.notifyAll();
					}

				}
			}
		};

		new Thread(producer).start();
		new Thread(producer).start();
		new Thread(producer).start();
		new Thread(consumer).start();
		new Thread(consumer).start();
	}

2. reentrant achieve lock of ReentrantLock

Lock java.util.concurrent.lock frame is an abstract locked by the lock of the lock () and unlock () method to achieve the display control of the lock, and Synchronize () is implicit lock control .
Reentrant lock, also known as recursive locks, referring to the same thread after the outer function to get a lock, there is still the inner recursive function code to get the lock, but will not be affected, in simple terms, the lock maintaining this one and get lock-related counters, if you have a thread lock lock once again, the acquisition counter is incremented, the end of the function call counter decrements, then lock to be released twice to get a real release. Already acquired a lock thread into the other synchronized block requires the same lock it will not be blocked.
ReentrantLock's Condition:

//阻塞当前线程,直到收到通知或者被中断(将当前线程加入到当前Condition对象的等待队列里)
//Block until signalled or interrupted
public final void await() throws InterruptedException;

/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
* 把在当前Condition对象的等待队列里的等待最久的线程,转移到当前Lock的等待队列里
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
*         returns {@code false}
*/
public final void signal() ;

ReentrantLock achieve production and consumption models:

/*
	 * 2.可重入锁ReentrantLock的实现
	 */
	public static void testProductConsumeByLock() {
		final Lock lock = new ReentrantLock();
		final Condition empty = lock.newCondition();
		final Condition full = lock.newCondition();

		final int size = 10;
		final Queue<String> queue = new ArrayDeque<String>(size);

		Runnable producer = new Runnable() {

			public void run() {
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				for (int i = 0; i < 20; i++) {
					lock.lock();
					try {
						if (queue.size() == size) {
							try {
								full.await();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						String msg = "生产消息:" + i;
						queue.add(msg);
						System.out.println(msg);
						empty.signal();
					} finally {
						lock.unlock();
					}
				}
			}
		};

		Runnable consumer = new Runnable() {
			public void run() {
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				while (true) {
					lock.lock();
					try {
						if (queue.isEmpty()) {
							try {
								empty.await();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						} else {
							String msg = queue.remove();
							System.out.println(msg + "已消费");
							full.signal();
						}
					} finally {
						lock.unlock();
					}
				}
			}
		};

		new Thread(producer).start();
		new Thread(producer).start();
		new Thread(producer).start();

		new Thread(consumer).start();
		new Thread(consumer).start();
	}

3. blocking queue BlockingQueue achieve

BlockingQueue that is blocking queue, it can be seen from the blocking of the word, and in some cases blocked access to the queue may cause blocking. There are cases blocked major following two:

  1. When the queue is full when carried into queues
  2. When the queue time for an empty queue operation

Therefore, when a thread is full of blocking queue enqueue operation will be blocked, unless there is another thread was a team operation, when a thread is blocked on an empty queue dequeue operation will be blocked, unless there was another thread enqueue operation.
From the above, blocking queue is thread-safe.

Here are some ways BlockingQueue interface:

operating Throws Specific value Clog time out
insert add(o) offer(o) put(o) offer(o, timeout, timeunit)
Remove remove(o) poll(o) take(o) poll(timeout, timeunit)
an examination element () peek(o)

Corresponding to each of these four methods are:

  1. ThrowsException: If the operation can not be performed immediately, an exception is thrown
  2. SpecialValue: If the operation is not performed immediately, will return a special value, usually true or false
  3. Blocks: If the operation can not be performed immediately, the operation will be blocked
  4. TimesOut: If the operation is not performed immediately, the operation will be blocked specified time, if the specified time is not performed, a special value, usually true or false

Let's look at production and consumption model implemented by blocking queue, where we use the take () and put () method, where there is no synchronization between the producers and the producers, consumers and the consumer, so there will be continuous generation and continuous consumption The phenomenon

/*
	 * 3.阻塞队列BlockingQueue实现
	 * 生产者消费者
	 * 使用阻塞队列实现
	 */
	public static void testProductConsumeByBlockingQueue() throws InterruptedException {

		/*
		 * 因为SynchronousQueue没有存储功能,因此put和take会一直阻塞,直到有另一个线程已经准备好参与到交付过程中。
		 * 仅当有足够多的消费者,并且总是有一个消费者准备好获取交付的工作时,才适合使用同步队列。
		 */
		//final BlockingQueue<String> queue = new SynchronousQueue<String>(true);

		// 使用有界阻塞队列
		final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);

		Runnable producer = new Runnable() {

			public void run() {
				for (int i = 0; i < 100; i++) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					String msg = "消息:" + i;
					try {
						queue.put(msg);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					System.out.println(msg + " 已发送");
				}
			}
		};

		Runnable consumer = new Runnable() {

			public void run() {
				while (true) {
					try {
						Thread.sleep(200);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					String msg = null;
					try {
						msg = queue.take();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(msg + "已消费");
				}
			}
		};

		new Thread(producer).start();
		new Thread(consumer).start();
	}

Semaphore implementation 4. semaphore

Semaphore number of threads can control access to the appropriate resources to achieve production and consumption model

import java.util.concurrent.Semaphore;

/**
 * @ClassName: BySemaphore
 * @Description: TODO(4.信号量Semaphore的实现(控制并发线程数的Semaphore:http://ifeve.com/concurrency-semaphore/))
 * @author root
 * @date 2020年3月30日
 */
public class BySemaphore {
	int count = 0;
	final Semaphore put = new Semaphore(5);// 初始令牌个数
	final Semaphore get = new Semaphore(0);
	final Semaphore mutex = new Semaphore(1);// 该信号量相当于锁

	public static void main(String[] args) {
		BySemaphore bySemaphore = new BySemaphore();
		new Thread(bySemaphore.new Producer()).start();
		new Thread(bySemaphore.new Consumer()).start();
		new Thread(bySemaphore.new Consumer()).start();
		new Thread(bySemaphore.new Producer()).start();
	}

	class Producer implements Runnable {
		@Override
		public void run() {
			for (int i = 0; i < 5; i++) {
				try {
					Thread.sleep(1000);
				} catch (Exception e) {
					e.printStackTrace();
				}
				try {
					put.acquire();// 注意顺序
					mutex.acquire();
					count++;
					System.out.println("生产者" + Thread.currentThread().getName() + "已生产完成,商品数量:" + count);
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					mutex.release();
					get.release();
				}

			}
		}
	}

	class Consumer implements Runnable {

		@Override
		public void run() {
			for (int i = 0; i < 5; i++) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				try {
					get.acquire();// 注意顺序
					mutex.acquire();
					count--;
					System.out.println("消费者" + Thread.currentThread().getName() + "已消费,剩余商品数量:" + count);
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					mutex.release();
					put.release();
				}
			}
		}
	}
}

5. Using message queue

This is a tricky way, the direct use of existing messaging middleware services (such as RocketMq, RabbitMq, Kafka, etc.), every minute to get. Manual smile

Published 111 original articles · won praise 57 · views 410 000 +

Guess you like

Origin blog.csdn.net/menghuanzhiming/article/details/105208668