- 临界区:对临界资源进行访问的那段代码称为临界区。为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。
- 同步与互斥:
同步:多个进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系。
互斥:多个进程在同一时刻只有一个进程能进入临界区。
使用信号量实现生产者消费者问题
- 使用一个容器来存放物品,只有当容器中还有空位置时,生产者才可以生产商品;只有当容器中物品数量大于0时,消费者才可以消费商品。
- 容器属于临界资源,所以需要使用一个互斥量mutex来控制对容器的互斥访问
- 为了同步生产者和消费者的行为,需要记录容器中物品的数量。数量可以使用信号量进行统计,这里需要使用两个信号量:empty和full。
- empty表示容器中可供放入物品的数量即空位置的数量,只有empty>0时,生产者才可以生产
- full表示容器中现存物品的数量,即可供消费者消费的物品的数量,只有full>0时,消费者才可以消费
- 注意:不能先对临界区加锁,再测试信号量。如果这么做了,那么可能会出现这种情况:生产者先对临界区加锁,然后对empty执行减1操作,此时empty变为0,那么生产者进入等待状态;消费者此时不能访问临界区,因为生产者对其加锁了,所以消费者不能执行empty+1操作,那么empty永远为0,导致生产者永远等待,不会释放锁,消费者因此也会永远等待下去。
class Cache {
private int cacheSize=0;
public Semaphore mutex;
public Semaphore full;//容器已装满的时候,full<=0,生产者需要等待
public Semaphore empty;//容器为空的时候,empty<=0,消费者需要等待
public Cache(int size) {
mutex=new Semaphore(1);//互斥量mutex来控制对缓冲区的互斥访问,通过0值来进行阻塞和唤醒
empty=new Semaphore(size);//容器中可供放入物品的数量即空位置的数量
full=new Semaphore(0);//容器中现存物品的数量,即可供消费的物品的数量
}
public int getCacheSize() {
return cacheSize;
}
public void produce() throws InterruptedException {
empty.acquire();//empty减1 减少了一个空位
mutex.acquire();
cacheSize++;
System.out.println(Thread.currentThread().getName()+"生产了一个商品,当前商品的数量为:"+cacheSize);
mutex.release();
full.release();//full+1 增加了一个商品
}
public void consume() throws InterruptedException {
full.acquire();//减少一件商品
mutex.acquire();
cacheSize--;
System.out.println(Thread.currentThread().getName()+"消费了一个商品,当前商品的数量为:"+cacheSize);
mutex.release();
empty.release();//空位数量+1
}
}
class Consumer implements Runnable{
private Cache cache;
public Consumer(Cache cache) {
this.cache=cache;
}
@Override
public void run() {
while(true) {
try {
cache.consume();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable{
private Cache cache;
public Producer(Cache cache) {
this.cache=cache;
}
@Override
public void run() {
while(true) {
try {
cache.produce();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ProducerConsumer {
public static void main(String[] args) {
Cache cache=new Cache(10);
Producer pro=new Producer(cache);
Consumer con=new Consumer(cache);
int proThreadCount=4,conThreadCount=4;
for(int i=0;i<proThreadCount;i++)
new Thread(pro).start();
for(int i=0;i<conThreadCount;i++)
new Thread(con).start();
}
}
使用管程实现生产者消费者问题