注:
S>0时:表示可供并发进程使用的资源数。
S<=0时:|S|表示因缺少该资源而自我阻塞的进程数。
在改进之后的wait函数中,首先会向操作系统申请资源,并且让资源数减一。如果资源分配完毕,那么就调用block原语将进程进行自我阻塞。
而在signal函数中,首先会释放资源,让资源数减一,之后进行判断,如果该信号量链表中仍有等待的进程被阻塞,那么就调用wakeup原语将其唤醒。
1.2.3 AND型信号量
AND型信号量是将进程在整个运行过程中的所有资源,一次性全部分配给进程,进程使用完后再一次性释放,只要一个进程尚未分配成功,其他所有为之分配的资源也不分配给它。也就说要么把它所请求的资源全部分配给进程,要么一个也不分配,这样的好处是可以很好的避免死锁现象。
使用该信号量机制时,wait和signal函数的实现如下:
wait(S1,S2,...,Sn)
{
while(true)
{
if(Si>=1&&...&&Sn>=1)
{
for(i=1;i<n;i++)
Si--;
break;
}
else
{
place the process in the waiting queue associated with.....
//如果没有申请到资源的话就挂到相应的阻塞队列里去
}
}
}
Ssignal(S1,S2,...Sn)
{
while(true)
{
for(i=1;i<=n;i++)
{
Si++;
remove all the process waiting in the queue associated with Si into the ready queue;
//去后备队列里唤醒或移除因Si资源阻塞的进程
}
}
}
以上就是对三种信号量机制的作用和实现原理的说明。
1.3 使用信号量机制解决问题
使用信号量机制所要解决的问题还是进程的互斥和同步问题。那么这两种问题又是如何实现的呢?
1.3.1 实现互斥关系
假设两个进程PA,PB具有互斥关系,也就是他们要使用通同一个临界资源,对于这种情况应该怎么做呢?我们可以设置一个mutex信号量,初值设为1,这样在最开始的时候,两个进程都能使用该资源,在进程PA使用资源的时候,首先会调用wait函数让资源数减一,wait函数完成之后会让信号量mutex-1,这样mutex的值此时为0,另一个进程PB就不能使用该资源了,在进程PA使用完资源以后会调用signal函数释放资源,让信号量mutex+1,mutex此时会重新变成1,另一个进程PB就可以使用该临界资源了。
semaphore mutex=1;
PA(){
while(1){
wait(mutex);
临界区
signal(mutex);
剩余区
}
}
PB(){
while(1){
wait(mutex);
临界区
signal(mutex);
剩余区
}
}
1.3.2 实现前驱关系
假设P1和P2有前驱关系,P1执行完,P2才能执行,那么这种应该怎么实现呢?这时可以设置一个公共的信号量S,初值设为0。
进程P1中:S1;signal(S);
进程P2中:wait(S);S2;
上面语句的意思是,先执行P1的语句,然后释放S,也就是S++,这样当P2执行完wait函数之后才可以执行,否则,不执行signal函数的话,S就为0,P2也无法执行,这样就实现了P1和P2的前驱关系。
接下来,我们将通过实验的方式来对上面描述的实现进程同步和互斥的问题进行实验。验证这种方式的实际使用性。
2 程序设计
2.1 需求分析
生活中我们经常遇到的生产者/消费者问题其实就是一个典型的进程同步问题,其中的生产者产生资源,消费者消耗资源。比如典型的买包子问题,我们可以通过它来模拟进程的同步。消费者与生产者进程之间的执行都依赖于另一个进程的消息,想要表现同步机制,这需要使用Java中的wait() / notify()方法实现同步机制。由于包子余量(资源数量)需要所有进程共享,因此任意时刻只能有一个进程访问缓冲器,这需要使用Java中的synchronized同步代码块实现,synchronized关键字的作用就是控制多个线程访问资源同步性的问题,它的三种使用方式是:修饰实例方法、修饰静态方法、修饰代码块。
如果方法或代码块用 synchronized 进行声明,那么对象的锁将保护整个方法或代码块,要调用这个方法或者执行这个代码块,必须获得这个对象的锁。而且,任何时候都只能有一个线程对象执行被保护的代码。
2.2 算法设计思路
我们以买包子问题为例,现设计2个厨师(生产者),2个顾客(消费者),包子铺包子存储上限为3(缓冲器大小)。包子初始值为0,此时所有买家进程会进入等待状态,所有的厨师进程会在包子余量不超过缓冲器大小前不停做包子,并唤醒买家进程已经有包子可吃了,直至缓冲器满了进入等待状态,而买家进程每吃掉一个包子后都会唤醒厨师进程可以继续做包子了。同时由于包子余量需要所有进程共享,保证任意时刻只能有一个进程访问缓冲器,因此所有进程方法都需要用synchronized声明。
3 源代码清单
3.1 缓冲器公共类
package com.common;
/**
* 定义缓冲器余量,生产者和消费者执行的方法
*/
public class BaoZi {
Integer count = 0; //记录包子总数
/**
* 生产包子(产生资源)
*
* @param name 厨师名
*/
public synchronized void makeBaoZi(String name) {
// 判断包子数是否大于3达到上限
while (count >= 3) {
System.out.println(name + ":包子已经达到上限了!");
try {
wait(); //包子容量达到3,超出上限,生产者进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++; //如果没有达到3就做一个包子
System.out.println(name + ":做了一个包子,还剩【" + count + "】个!");
notifyAll(); //有包子资源了,唤醒消费者购买包子
}
/**
* 购买包子(使用资源)
*
* @param name
*/
public synchronized void buyBaoZi(String name) {
// 判断包子资源数是否等于0,
while (count == 0) {
System.out.println(name + ":没有包子资源了!");
try {
wait(); //没有包子资源,消费者进程进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--; //包子资源数量减一
System.out.println(name + ":购买了一个包子,包子资源剩余【" + count + "】个!");
notifyAll(); //包子数未达到上限,让生产者厨师做包子,(唤醒所有等待该资源的进程),
}
}
3.2 生产者进程类
package com.producer;
import com.common.BaoZi;
/**
* 生产者进程
*/
public class BaoZiPu extends Thread {
private String name; //厨师名字
private BaoZi baoZi; //缓冲区包子资源
public BaoZiPu() {
}
public BaoZiPu(BaoZi baoZi, String name) {
this.baoZi = baoZi;
this.name = name;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(2000); //休眠两秒钟
baoZi.makeBaoZi(name); //生产一个包子资源
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.3 消费者进程类
package com.consumer;
import com.common.BaoZi;
/**
* 消费者进程,消耗包子资源
*/
public class Customer extends Thread {
private String name;
private BaoZi baoZi;
public Customer() {
}
public Customer(BaoZi baoZi, String name) {
this.baoZi = baoZi;
this.name = name;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(2000); //休眠两秒钟
baoZi.buyBaoZi(name); //消耗一个包子资源
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.4 测试类
本人从事网路安全工作12年,曾在2个大厂工作过,安全服务、售后服务、售前、攻防比赛、安全讲师、销售经理等职位都做过,对这个行业了解比较全面。
最近遍览了各种网络安全类的文章,内容参差不齐,其中不伐有大佬倾力教学,也有各种不良机构浑水摸鱼,在收到几条私信,发现大家对一套完整的系统的网络安全从学习路线到学习资料,甚至是工具有着不小的需求。
最后,我将这部分内容融会贯通成了一套282G的网络安全资料包,所有类目条理清晰,知识点层层递进,需要的小伙伴可以点击下方小卡片领取哦!下面就开始进入正题,如何从一个萌新一步一步进入网络安全行业。
学习路线图
其中最为瞩目也是最为基础的就是网络安全学习路线图,这里我给大家分享一份打磨了3个月,已经更新到4.0版本的网络安全学习路线图。
相比起繁琐的文字,还是生动的视频教程更加适合零基础的同学们学习,这里也是整理了一份与上述学习路线一一对应的网络安全视频教程。
网络安全工具箱
当然,当你入门之后,仅仅是视频教程已经不能满足你的需求了,你肯定需要学习各种工具的使用以及大量的实战项目,这里也分享一份我自己整理的网络安全入门工具以及使用教程和实战。
项目实战
最后就是项目实战,这里带来的是SRC资料&HW资料,毕竟实战是检验真理的唯一标准嘛~
面试题
归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!