管程 (英语:Monitors,也称为监视器) :是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件或一群变量。管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程实现很大程度上简化了程序设计。管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。
组成元素::
运行机制:
当一个线程执行管程中的一个子程序(例如:出队列、入队列)时,称为占用(occupy)该管程, 管程的实现确保了在一个时间点,最多只有一个线程占用了该管程,这是管程的互斥锁访问性质。当线程要调用一个定义在管程中的子程序时,必须等到已经没有其它线程在执行管程中的某个子程序。在管程的简单实现中,编译器为每个管程对象自动加入一把私有的互斥锁。该互斥锁初始状态为解锁,在管程的每个公共子程序的入口给该互斥锁加锁,在管程的每个公共子程序的出口给该互斥锁解锁。
----------------------以上来自维基百科-----------------------
上述运行机制简单可以归纳如下(便于理解):
- 生产者在缓存队列满时不再生产,进入等待队列
- 消费者在缓存队列为空时不再消费,进入等待队列
- 当生产者生产可消费的资源时通知处于等待队列消费者,进行消费
- 当消费者没有资源时通知等待生产者生产
下面利用管程来实现多线程中比较经典的生产者消费者模型。
- 管程主实现类:
package pubsub;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName : MQueue
* @Description:
* @Author: liulianglin
* @Date: 2021/4/2 12:04
* @Version : 1.0
*/
public class MQueue<T> {
/**
* 共享变量
*/
private final Lock lock = new ReentrantLock();
/**
* 条件变量: 当生产者生产数据遇到队列满时,生产者进入该等待队列,等待消费者消费共享数据后唤醒它。
*/
private final Condition queueFullWait = lock.newCondition();
/**
* 条件变量: 当消费者消费数据遇到队列为空时,消费者进入等待队列,等待生产者生产数据后唤醒它
*/
private final Condition queueEmptyWait = lock.newCondition();
/**
* 共享数据的队列
*/
private final List<T> objectList = new ArrayList<>();
/**
* 队列容量
*/
private int listCapacity;
/**
* 队列中当前存储数据的数量
*/
private int curListSize;
MQueue(int listCapacity){
this.listCapacity = listCapacity;
}
/**
* 生产数据:入队列
*/
public void produce(T obj){
//获取锁
lock.lock();
try {
if (curListSize >= listCapacity){
//如果当前队列大小已经到达最大容量,则生产者线程进入等待队列中
System.out.println("队列满了,生产者进入等待队列...");
queueFullWait.await(1, TimeUnit.MILLISECONDS);
}
objectList.add(obj);
curListSize++;
//通知处于queueEmptyWait等待状态的线程
System.out.println("生产完一条数据,通知处于queueEmptyWait等待状态的线程...");
queueEmptyWait.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}
/**
* 消费数据:出队列
*/
public T consume(){
T result = null;
lock.lock();
try{
if (curListSize <= 0){
//如果当前队列为空,则进入等待队列中
System.out.println("当前队列为空,消费者进入等待队列...");
queueEmptyWait.await(1, TimeUnit.MILLISECONDS);
}
//移除第一个
result = objectList.remove(0);
curListSize--;
//通知处于queueFullWait等待队列中的生产者
System.out.println("消费者消费完一条数据,通知处于queueFullWait等待队列中的生产者...");
queueFullWait.signal();
}catch (Exception ex){
ex.printStackTrace();
}finally {
lock.unlock();
}
return result;
}
}
- 生产者线程:
package pubsub;
/**
* @ClassName : ProducerThread
* @Description:
* @Author: liulianglin
* @Date: 2021/4/2 12:33
* @Version : 1.0
*/
public class ProducerThread implements Runnable {
private final MQueue mQueue;
/**
* 需要生产的数据总量
*/
private final int needProduceCount;
ProducerThread(MQueue mQueue, int needProduceCount){
this.mQueue = mQueue;
this.needProduceCount = needProduceCount;
}
@Override
public void run() {
//生产数据
for (int i = 0; i <= needProduceCount; i++) {
//构造数据
Object object = new Object();
mQueue.produce(object);
System.out.println(i+ "-- 生产: "+object );
}
}
}
- 消费者线程:
package pubsub;
/**
* @ClassName : ConsumerThread
* @Description:
* @Author: liulianglin
* @Date: 2021/4/2 12:37
* @Version : 1.0
*/
public class ConsumerThread implements Runnable{
private final MQueue mQueue;
private final int needConsumeSize;
ConsumerThread(MQueue mQueue, int needConsumeSize){
this.mQueue = mQueue;
this.needConsumeSize = needConsumeSize;
}
@Override
public void run() {
for (int i = 0; i <= needConsumeSize; i++) {
Object result = mQueue.consume();
System.out.println(i+ "-- 消费:"+result);
}
}
}
- 主测试类:
package pubsub;
/**
* @ClassName : MainTest
* @Description:
* @Author: liulianglin
* @Date: 2021/4/2 12:53
* @Version : 1.0
*/
public class MainTest {
public static void main(String[] args) {
MQueue mQueue = new MQueue(1);
new Thread(new ProducerThread(mQueue, 2)).start();
new Thread(new ConsumerThread(mQueue, 2)).start();
}
}
- 运行结果: