问题描述
生产者和消费者在同一时间段内共用同一个存储空间,生产者向空间里存放数据,而消费者取用数据。
问题分析
生产者:负责生产数据,若此时容器已满,则停止生产(进入阻塞状态,释放锁);若容器未满,则 生产数据,并唤醒所有线程。
消费者:负责消费数据,若此时容器已空,则排队等候(进入阻塞状态,释放锁);若容器为空,则消费数据,并唤醒所有线程。
基于synchronized 实现
package thread.question;
import java.util.ArrayList;
import java.util.List;
/**
* 生产者消费者问题
*/
public class ProductorAndCustom<T> {
List<T> list = new ArrayList<>();
int MAX = 10;
int cur = 0;
public synchronized void put(T t) {
while (cur == MAX) {
try {
//阻塞
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(t);
System.out.println(Thread.currentThread().getName() + "生产了一个" + t);
cur++;
System.out.println("此时还剩" + cur + t);
//唤醒消费者线程,也有可能是生产者线程
this.notifyAll();
}
public synchronized T get() {
T t = null;
while (cur == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t = list.remove(0);
cur--;
System.out.println(Thread.currentThread().getName() + "消费了一个" + t);
System.out.println("此时还剩" + cur + t);
//唤醒生产者线程
this.notifyAll();
return t;
}
public static void main(String[] args) {
ProductorAndCustom<String> p = new ProductorAndCustom<>();
//创建生产者线程
for (int i = 0; i < 2; i++) {
new Thread(() -> {
while (true) {
p.put("蛋糕");
}
}, "生产者" + i).start();
}
for (int i = 0; i < 5; i++) {
new Thread(() -> {
String d = p.get();
}, "消费者" + i).start();
}
}
}
结果输出
基于ReentrantLock和Condition实现
基于synchronized每次唤醒的线程可能是消费者,也可能是生产者,而基于condition的await()和signal()方法,可以指定哪些线程被唤醒,注意的是调用await()和signal()时,必须在lock()范围内。
代码如下:
package thread.question;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}