Horse Soldier High Concurrent Programming Series Records
https://www.bilibili.com/video/av11076511/?spm_id_from=333.788.videocard.4
Interview Question: Write a fixed capacity synchronization container (put method waits when full, get method waits when empty), have put and get method, and getCount method.
Supports blocking calls from 2 producer threads and 10 consumer threads
way1
Use wait and notify/notify All to implement
import java.util.LinkedList; import java.util.concurrent.TimeUnit; public class MyContainer1<T> { final private LinkedList<T> lists = new LinkedList<>(); final private int MAX = 10; //Up to 10 elements (capacity 10) private int count = 0; public synchronized void put(T t) { while(lists.size() == MAX) { //Think about why while instead of if? /* if, threads A and B are thrown into the container, because the container is full, A and B are waiting, and a consumer thread takes one away. At this time, A and B are woken up (wake-up does not necessarily get the lock, need to compete with other threads). A is ready to throw into the container, but has not yet been thrown. Another thread B threw it into container 1, and thread A made a mistake when it was going to throw it, which exceeded the capacity of the container. if If the container is full, wait releases the lock. Next time you execute it, you will not judge whether the container is full or not, leading to errors. (When all threads are awakened, they continue to execute from wait, not from the start. So the solution is that while(),this.wait() needs to be checked again when it goes down (if only once, while will always judge) spurious wakeup wait()After the call, the thread calling the method goes to sleep. wait()When the lock is released after the call, if all the other threads are asleep, they will not take the initiative to compete for the newly released lock. Only notify() or notifyAll() will threads enter the competition queue, and if other threads release locks, they will compete for locks. */ try { this.wait(); //In effective java, wait() is used together in 99.9% of cases, not if } catch (InterruptedException e) { e.printStackTrace(); } } lists.add(t); ++count; this.notifyAll();//Notify the consumer thread to consume /* this.notify() is not used because it only wakes up one of the threads waiting for the lock, and it is possible to wake up the producer P??? Because the latter only wakes up any thread waiting on that resource, and if it wakes up the producer thread, the thread is deadlocked. notifyAll()Wake up all threads waiting for the lock. */ // If this is the case of a producer and a consumer, there is no problem in using notify, and it is more efficient. } public synchronized T get() { T t = null; while(lists.size() == 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } t = lists.removeFirst();//Take one out. count --; this.notifyAll(); //Notify the producer to proceed with production return t; } public static void main(String[] args) { MyContainer1<String> c = new MyContainer1<>(); //Start 10 consumer threads for(int i=0; i<10; i++) { new Thread(()->{ for(int j=0; j<5; j++) System.out.println(c.get()); }, "c" + i).start(); } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //Start 2 producer threads for(int i=0; i<2; i++) { new Thread(()->{ for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j); }, "p" + i).start(); } } }
way2
Implementation using Lock and Condition
Comparing the two approaches, Condition s can specify more precisely which threads are awakened.
import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyContainer2<T> { final private LinkedList<T> lists = new LinkedList<>(); final private int MAX = 10; //Up to 10 elements private int count = 0; private Lock lock = new ReentrantLock(); private Condition producer = lock.newCondition(); private Condition consumer = lock.newCondition(); public void put(T t) { try { lock.lock(); while(lists.size() == MAX) { //Think about why while instead of if? producer.await();// The producer waits to note that await() is not wait() } lists.add(t); ++count; consumer.signalAll(); //Notify the consumer thread to consume } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public T get() { T t = null; try { lock.lock(); while(lists.size() == 0) { consumer.await();//Consumers wait } t = lists.removeFirst(); count --; producer.signalAll(); //Notify the producer to proceed with production } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } return t; } public static void main(String[] args) { MyContainer2<String> c = new MyContainer2<>(); //Start the consumer thread for(int i=0; i<10; i++) { new Thread(()->{ for(int j=0; j<5; j++) System.out.println(c.get()); }, "c" + i).start(); } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //Start the producer thread for(int i=0; i<2; i++) { new Thread(()->{ for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j); }, "p" + i).start(); } } }
summary
Wait notify all synchronized used together
ReentrantLock Condition await is used together