生产消费模型是多线程安全的典型例子,当初实现的时候使用的是一个数组或者队列,加上synchronized关键字,代码量比较多,后来学习了阻塞队列,发现使用阻塞队列来实现代码简洁不少,这样在面试的时候,面试官需要你实现一个生产消费模型也会好写一些,下面我给出原始和阻塞队列实现的代码供参考:
1.原始实现
package Concurrent;
import java.util.PriorityQueue;
import java.util.Queue;
public class TestObjProCon {
//定义队列的长度
private int queueSize=10;
//定义一个队列,这里使用的是优先队列PriorityQueue
private Queue<Integer> queue=new PriorityQueue<Integer>(queueSize);
public static void main(String[] args){
TestObjProCon test=new TestObjProCon();
Consumer c=test.new Consumer();
Productor p=test.new Productor();
c.start();
p.start();
}
//消费者类
class Consumer extends Thread{
@Override
public void run(){
comsume();
}
private void comsume() {
//使用一个while循环进行监听
while(true){
//同步队列
synchronized (queue) {
//判断队列内部元素个数,是否为0,为0等待,否则唤醒其他队列
while(queue.size()==0){
try{
System.out.println("队列为空,不能取,请等待");
//调用wait方法,使取的队列进入阻塞
queue.wait();
//调用wait方法后,线程中断抛出InterruptedException执行catch语句
}catch(InterruptedException e){
e.printStackTrace();
//并唤醒生产队列,即切换队列
queue.notify();
}
}
//若不为空,则可以进行consume
//poll()每次都是移走队首元素
queue.poll();
//并唤醒生成线程,因为poll一次代表取了一次,即可以继续生产
queue.notify();
System.out.println("队列剩余的元素个数为:"+queue.size());
}
}
}
}
//生产者类
class Productor extends Thread{
public void run(){
produce();
}
}
public void produce() {
while(true){
//同步队列
synchronized (queue) {
while(queue.size()==queueSize){
try{
System.out.println("队列已经满了,不能继续生产了");
queue.wait();//使生产队列进入阻塞
}catch(InterruptedException e){
e.printStackTrace();
queue.notify();//唤醒消费队列
}
}
//否则可以继续生产
queue.offer(1);//调用offer方法,每次插入一个元素
queue.notify();//插入一个元素后即可以唤醒消费线程,执行消费
System.out.println("此时队列里面的元素为:"+queue.size());
}
}
}
}
2.阻塞队列实现:
package Concurrent;
import java.util.concurrent.PriorityBlockingQueue;
public class TestPriorityBlockingQueue {
private int queueSize=10;
private PriorityBlockingQueue<Integer> queue=new PriorityBlockingQueue<Integer>(queueSize);
public static void main(String[] agrs){
TestPriorityBlockingQueue test=new TestPriorityBlockingQueue();
Consumer c=test.new Consumer();
Productor p=test.new Productor();
c.start();
p.start();
}
class Consumer extends Thread{
@Override
public void run(){
//重写run方法,在run方法中写自己的线程代码和相应方法
comsume();
}
private void comsume() {
//仍需要监听,而简化的是不需要使用synchronized同步锁了
while(true){
try{
//内部take已经实现了lock机制
queue.take();
System.out.println("队列元素剩余");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
class Productor extends Thread{
@Override
public void run(){
produce();
}
private void produce() {
while(true){
try {
//内部已经实现Lock机制,不要自己使用Synchronized和wait,notify
queue.put(1);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}
}
}
这里我简单说一下:PriorityBlockingQueue优先阻塞队列,take方法和put方法内部已经实现锁机制,采用的是ReentrantLock,内部已经实现Lock机制,不要自己使用Synchronized和wait,notify进行线程间通信,下面是相应的源码:
take:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ( (result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
}
return result;
}
put:
public void put(E e) {
offer(e); // never need to block
}
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
int n, cap;
Object[] array;
while ((n = size) >= (cap = (array = queue).length))
tryGrow(array, cap);
try {
Comparator<? super E> cmp = comparator;
if (cmp == null)
siftUpComparable(n, e, array);
else
siftUpUsingComparator(n, e, array, cmp);
size = n + 1;
notEmpty.signal();
} finally {
lock.unlock();
}
return true;
}