“请写出一个生产者消费者模型。一个生产者,一个消费者,生产者生产一个,消费者消费一个”。
这是一个典型的多线程面试题,考验对锁和多线程对运用。要想实现,有两种常见模式。一种是通过wait() / notify(),另一种是通过 ReentrantLock 的 condition。
wait()和notify()实现
Object的wait() / notify()是第一种实现方式。
这里有必要提一下Thread.sleep()和Object.wait()的区别。
sleep和wait区别
1、Thread.sleep()不会导致锁行为的改变,如果当前线程是拥有锁的,那么Thread.sleep()不会让线程释放锁。如果能够帮助你记忆的话,可以简单认为和锁相关的方法都定义在Object类中,因此调用Thread.sleep()是不会影响锁的相关行为。
2、Thread.sleep和Object.wait都会暂停当前的线程,对于CPU资源来说,不管是哪种方式暂停的线程,都表示它暂时不再需要CPU的执行时间。OS会将执行时间分配给其它线程。区别是调用wait后,需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间。
wait和notify
wait():放弃锁,使自己处于等待状态,让其他线程执行。
notify():向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
实现代码如下:
public class Demo {
static volatile int i = 0;
static final Object LOCK = new Object();
public static void add() throws InterruptedException {
synchronized (LOCK) {
while (i == 0) {
System.out.print("add\t");
System.out.println(++i);
LOCK.notify();
LOCK.wait();
}
}
}
public static void sub() throws InterruptedException {
synchronized (LOCK) {
while (i == 1) {
System.out.print("sub\t");
System.out.println(--i);
LOCK.notify();
LOCK.wait();
}
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
sub();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
signal()和await()实现
ReentrantLock的signal() / await()是第二种实现方式。利用的是 ReentrantLock 内部的同步对列和条件队列来实现。这个原理需要可以在后面讲解。
public class Demo2 {
static volatile int i = 0;
static final ReentrantLock LOCK = new ReentrantLock();
static final Condition condition = LOCK.newCondition();
public static void add() throws InterruptedException {
LOCK.lock();
try {
while (i == 0) {
Thread.sleep(1000);
System.out.print("add\t");
System.out.println(++i);
condition.signal();
condition.await();
}
} finally {
LOCK.unlock();
}
}
public static void sub() throws InterruptedException {
LOCK.lock();
try {
while (i == 1) {
Thread.sleep(1000);
System.out.print("sub\t");
System.out.println(--i);
condition.signal();
condition.await();
}
} finally {
LOCK.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (true) {
try {
add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while (true) {
try {
sub();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}