问题
写一个固定容量同步容器,拥有put和get方法,以及getCount方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用。
解法一
这是一个典型的生产者消费者问题,可以通过wait()和notifyAll()方法来实现。
public class MyContainer4 {
private LinkedList<String> list = new LinkedList<>();
private static final int MAX_SIZE = 10;
public synchronized int getCount() {
return list.size();
}
public synchronized void put(String s) {
while (getCount() >= MAX_SIZE) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " put " + s);
list.add(s);
this.notifyAll();
}
public synchronized String get() {
while (getCount() == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String s = list.removeFirst();
this.notifyAll();
return s;
}
public static void main(String[] args) {
MyContainer4 myContainer4 = new MyContainer4();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = myContainer4.get();
System.out.println(Thread.currentThread().getName() + " get: " + s);
}
}, "consumer" + i).start();
}
for (int i = 0; i < 2; i++) {
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myContainer4.put(Thread.currentThread().getName() + "product");
}
}, "producer" + i).start();
}
}
}
解法二
上面的解法有一个弊端,当消费者从list中获取数据之后,会调用notifyAll()方法来唤醒所有的线程。其实消费者取完数据之后,只需要唤醒生产者线程即可,没必要唤醒其他的消费者。为了更加精确地唤醒生产者或消费者,可以使用ReentrantLock来实现。
public class MyContainer5 {
private LinkedList<String> list = new LinkedList<>();
private static final int MAX_SIZE = 10;
Lock lock = new ReentrantLock();
Condition productorCondition = lock.newCondition();
Condition consumerCondition = lock.newCondition();
public void put(String s) {
try {
lock.lock();
while (list.size() >= MAX_SIZE) {
productorCondition.await(); // 进入生产者等待队列
}
System.out.println(Thread.currentThread().getName() + " put " + s);
list.add(s);
consumerCondition.signalAll(); // 生产一个元素后,唤醒消费者等待队列中的线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public String get() {
String s = null;
try {
lock.lock();
while (list.size() == 0) {
consumerCondition.await(); // 进入消费者等待队列
}
s = list.removeFirst();
productorCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return s;
}
public static void main(String[] args) {
MyContainer5 myContainer4 = new MyContainer5();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = myContainer4.get();
System.out.println(Thread.currentThread().getName() + " get: " + s);
}
}, "consumer" + i).start();
}
for (int i = 0; i < 2; i++) {
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myContainer4.put(Thread.currentThread().getName() + "product");
}
}, "producer" + i).start();
}
}
}