JAVA实现生产者-消费者,使用信号量Semaphore
简单看看什么是信号量:
Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。它其中的常用方法:
参数为许可证数量。
void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。(相当于加锁)
void release():释放一个许可,将其返回给信号量。(相当于释放锁)
int availablePermits():返回此信号量中当前可用的许可数。
boolean hasQueuedThreads():查询是否有线程正在等待获取。
先建个Buffer类用于做缓存区以及实现生产方法和校服方法
public class Buffer {
//作为生产资料存在ArrayList缓存区里
private int count;
//定义一个 ArrayList 作为缓存区
private ArrayList<Integer> pool = new ArrayList<>();
private Semaphore mutex = new Semaphore(1); //定义只有一个许可证的信号量用做互斥信号量
private Semaphore empty = new Semaphore(10); //定义有10个 许可证的信号量用做空缓存区
private Semaphore full = new Semaphore(0); //定义0个许可证的信号量,用于记录库存null还是not null
//定义Wait方法 用于实现加锁功能
public int Wait(Semaphore semaphore) {
try {
//从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。 实现了加锁的功能
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回此信号量中当前可用的许可数。
return semaphore.availablePermits();
}
//定义Wait方法 用于实现解锁功能
public int Notify(Semaphore semaphore){
//释放一个许可,将其返回给信号量。(相当于释放锁)
semaphore.release();
return semaphore.availablePermits();
}
/**生产方法
* @param area**/
public void production(JTextArea area,int sleepTime) {
while(true) {
if (!algorithmGUI.sign) {
break;
}
if (empty.availablePermits() == 0) {
System.out.println("库存已满等待消费");
area.append(" "+"库存已满等待消费"+"\n\n");
}
/**线程休眠0.5秒钟**/
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 通过 Wait(empty) 过得一个许可证 ,此时empty的许可证数量减一,直到许可证数量为0时线程阻塞,达到判断缓存区已满的效果
* Wait(mutex); 因为 mutex 只有一个许可证,当生产者获得 mutex的许可证时会让许可证数量为0 消费者就无法获取到 mutex的许可证
* 导致消费者进入阻塞状态等待生产者归还许可证达到同步效果
* */
Wait(empty);
Wait(mutex);
pool.add(++count);
area.append(" "+Thread.currentThread().getName()+"生产了第" + count + "个产品" + "\t库存容量:" + pool.size() + "\n\n");
/**
* Notify(mutex); 归还mutex的许可证,当消费者获得许可证后进行消费
* Notify(full); full的开始许可证数量为0,使用release()方法 让full的许可证加一,让消费者可以有许可证获取,达到判断库存是否为空还是非空
* */
Notify(mutex);
/** 记录已经生产个数 **/
Notify(full);
}
}
//消费方法
public void consumption(JTextArea area,int sleepTime) {
while(true) {
if (!algorithmGUI.sign) {
break;
}
if (full.availablePermits() == 0) {
System.out.println("库存为空等待生产");
area.append(" "+Thread.currentThread().getName()+"进行消费结果失败----->"+"库存为空等待生产\n\n");
}
/**线程休眠0.5秒钟**/
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
/** 判断 full是否有许可证可以获取,如果有则说明库存不为空消费者可以进行消费,否则库存为空,等待生产者生产者 **/
Wait(full);
Wait(mutex);
//获得产品
int temp = pool.get(0);
//消费产品将产品从list中移除
pool.remove(0);
area.append(" "+Thread.currentThread().getName() + "消费了第" + temp + "个产品" + "----->库存容量: " + pool.size() + "\n\n");
/**
* Notify(empty); 每当消费者消费一个产品,执行一次Notify(empty)方法,归还empty一个许可证让缓存区数量加一
* Notify(mutex); 归还mutex的许可证让生产者可以进行生产
* **/
Notify(empty);
Notify(mutex);
}
}
}
建个consumer类实现Runnable让它开启线程调用生产方法,实现生产者的生产
//消费者线程
public class consumer implements Runnable{
Buffer bf;
JTextArea area;
int sleepTime;
public consumer(Buffer bf, JTextArea area,int sleepTime){
this.bf = bf;
this.area = area;
this.sleepTime = sleepTime;
}
@Override
public void run() {
bf.consumption(area,sleepTime);
}
}
建个producer类实现Runnable让它开启线程调用消费方法,实现消费者的消费
//生产者线程
public class producer implements Runnable{
Buffer bf;
JTextArea area;
int sleepTime;
public producer(Buffer bf, JTextArea area, int sleepTime){
this.bf = bf;
this.area = area;
this.sleepTime = sleepTime;
}
@Override
public void run() {
bf.production(area,sleepTime);
}
}
主方法----->创建多个线程开始进行测试
public class test {
public static void main(String[] args) {
buffer bf = new buffer();
new Thread(new producer(bf)).start();
new Thread(new consumer(bf),"1号消费者").start();
new Thread(new consumer(bf),"2号消费者").start();
new Thread(new consumer(bf),"3号消费者").start();
}
}
执行的结果
修改生产方法和消费方法里面线程休眠的时间,让生产方法生产速度大于消费速度或者消费速度大于生产速度就可以出现临界区