在多线程面试的过程中我们经常会被问到关于生产者消费者的问题,然后生产者消费者模型在工作中也是常常使用到的,即便是很多封装的库那也是常常被使用的。老规矩先抛出几个常规面试问题 1.手写代码实现生产者消费者的方式有哪些? 2.Synchronized 和 ReentrantLock 的区别 3.什么情况下会死锁? 生产者消费者模型中我们可以将一部分线程理解为工人 一部分线程理解为卡车,需要将工人生产出的产品运走(消费)。那么我们是否需要一个或者几个仓库用来存这些生产出的产品。那么我们可以快速构建出几个角色来完成这个场景,其实很这种场景很多我被问到的情况也很多,但是面试官不会直接问你使用多线程怎么解决。有问车站同时卖票的的多个窗口卖10000张票的问题。有的问,天网中快速识别一个人的面部,分为ABCD 四个工序,每个工序耗时不同,怎么解决。这些都不用怕!!多线程帮你解决!回到上面三个角色的问题三个角色分别为生产者,消费者,队列 ! 如下面流程图
public class MyContainer<T> {
private LinkedList<T> container;
private int count = 0;
private int MAX_SIZE = 20;
public MyContainer(int MAX_SIZE) {
this.container = new LinkedList<>();
this.MAX_SIZE = MAX_SIZE;
}
public int getCount() {
return count;
}
/**
* 多线程调用
*/
public void put(T t) {
synchronized (container) {
while (count == MAX_SIZE) {
try {
container.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
container.add(t);
++count;
container.notifyAll();
System.out.println("ThreadName :" + Thread.currentThread().getName() + "--result---:" + t.toString());
}
}
public T get() {
synchronized (container) {
while (count == 0) {
try {
container.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T first = container.removeFirst();
count--;
System.out.println("ThreadName :" + Thread.currentThread().getName() + "--result---:" + first.toString());
container.notifyAll();
return first;
}
}
}
我们自定义一个队列,使用LinkedList 来实现的,当然linkedList 是线程不安全的,当我们生产者往队列里put(T t) 的时候这时候会去判断当前仓库是否已经满了,如果满了当前线程就会在这里阻塞,其他的生产者也不能进来,这时候wait()会释放当前实例对象的锁,其他在在锁池等待的线程等锁进来后,抢锁成功的线程会拥有执行权。但是阻塞的这个线程会一直在这里阻塞,等待其他持有该同样的锁的线程唤醒。如果没有阻塞,那么生产者会一直往队列中添加产品。这里的notifyall,同样会唤醒阻塞的生产者和阻塞的消费者。 如果生产者队列满了当然唤醒生产者也没有用所以直到唤醒消费者会去消费调用get() 方法,当然每个get 方法只允许一个线程进来,当队列为空的时候也会阻塞,即使其他消费者线程获得锁进来也会阻塞。
public class MyClass {
public static void main(String[] args) {
MyContainer<Bean> myContainer = new MyContainer<>(50);
Thread thread0 = new Thread(new Productor(myContainer), "生产者A");
// Thread thread1 = new Thread(new Productor(myContainer), "生产者B");
// Thread thread2 = new Thread(new Productor(myContainer), "生产者C");
// Thread thread3 = new Thread(new Productor(myContainer), "生产者D");
Thread thread4 = new Thread(new Consumer(myContainer), "消费者1");
Thread thread5 = new Thread(new Consumer(myContainer), "消费者2");
thread0.start();
// thread1.start();
// thread2.start();
// thread3.start();
thread4.start();
thread5.start();
}
private static class Productor implements Runnable {
private MyContainer<Bean> myContainer;
public Productor(MyContainer<Bean> myContainer) {
this.myContainer = myContainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Bean bean = new Bean();
bean.setPName("产品" + i);
bean.setLineName(Thread.currentThread().getName());
myContainer.put(bean);
}
}
}
private static class Consumer implements Runnable {
private MyContainer<Bean> myContainer;
public Consumer(MyContainer<Bean> myContainer) {
this.myContainer = myContainer;
}
@Override
public void run() {
while (true) {
myContainer.get();
}
}
}
}
好了我们来看一下结果
/**
* 自己定义同步队列
*/
public class MyContainer<T> {
private LinkedList<T> container;
private int count = 0;
private int MAX_SIZE = 20;
private ReentrantLock lock;
private final Condition productor;
private final Condition consumer;
public MyContainer(int MAX_SIZE) {
this.container = new LinkedList<>();
this.MAX_SIZE = MAX_SIZE;
lock = new ReentrantLock();
productor = lock.newCondition();
consumer = lock.newCondition();
}
public int getCount() {
return count;
}
/**
* 多线程调用
*/
public void put(T t) {
try {
lock.lock();
while (count == MAX_SIZE) {
try {
productor.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
container.add(t);
++count;
consumer.signalAll();
System.out.println("ThreadName :" + Thread.currentThread().getName() + "--result---:" + t.toString());
} finally {
lock.unlock();
}
}
public T get() {
try {
lock.lock();
while (count == 0) {
try {
consumer.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T first = container.removeFirst();
count--;
System.out.println("ThreadName :" + Thread.currentThread().getName() + "--result---:" + first.toString());
productor.signalAll();
return first;
} finally {
lock.unlock();
}
}
}
死锁: 我们说互斥锁,就是Synchronized 时互斥锁,ReentrantLock 也是互斥锁,当然你可以按照你的想法去实现你的互斥锁。当线程1 持有线程2 所需要资源,当线程2持有线程1 锁持有的资源,那么互相等待那么就会造成死锁。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (mlock1) {
System.out.println("线程1.。。持有锁1.....");
synchronized (mLock2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1.。持有锁2.。。。。");
}
System.out.println("线程1.。释放锁 2.。。。。");
}
System.out.println("线程1.。释放锁 1.。。。。。");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (mLock2) {
System.out.println("线程2.。持有锁2.....");
synchronized (mlock1) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2.。。持有锁2.。。。。");
}
System.out.println("线程2.。。释放锁 1.。。。。");
}
System.out.println("线程2.。。释放锁 2.。。。。。");
}
});
thread.start();
thread2.start();