我想教你实现Java进程同步,因为我是浪漫的程序员!_java @transaction 进程同步

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

wait(S){
    while(S<=0);
    S--;
}

signal(S){
    S++;
}

整型信号量的缺点:

从上面的程序中,我们其实可以发现一个问题,就是当信号量S是小于0的时候,在wait函数中,程序会一直的死循环下去。这样进程就会处于一个“忙等”的状态。这也是使用整型信号量机制的不足之处,那么如果要解决“忙等”,就要让程序进行“让权等待”。也就是在进行无法使用资源的时候,释放处理机,避免长时间占用。

1.2.2 结构体型信号量

对于上面出现的问题,我们应该让哪个进程访问临界资源呢?这个时候结构体信号量就出现了,我们可以使用一个链表来解决这个问题。使用一个结构体来描述资源,这个结构体长这样:

typedef struct{
    int value;
    struct process_control_block *list;
}semaphore;

在这个结构体里边有两个变量,一个是value,用来记录资源的个数,第二个是指针,指向下一个要使用临界资源的进程。

通过使用结构体型信号量,wait和signal函数就进行了一些改进,如下这样:

wait(semaphore *S){
    S->value--;
    if(S->value<0)  block(S->list);
}

signal(semaphore *S){
    s->value++;
    if(S->value<=0)
      wakeup(S->list);
}

在程序中,S->value的初值表示系统中某类资源的数目,可称为资源信号量。

注:

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;
    }



本人从事网路安全工作12年,曾在2个大厂工作过,安全服务、售后服务、售前、攻防比赛、安全讲师、销售经理等职位都做过,对这个行业了解比较全面。


最近遍览了各种网络安全类的文章,内容参差不齐,其中不伐有大佬倾力教学,也有各种不良机构浑水摸鱼,在收到几条私信,发现大家对一套完整的系统的网络安全从学习路线到学习资料,甚至是工具有着不小的需求。


最后,我将这部分内容融会贯通成了一套282G的网络安全资料包,所有类目条理清晰,知识点层层递进,需要的小伙伴可以点击下方小卡片领取哦!下面就开始进入正题,如何从一个萌新一步一步进入网络安全行业。


![](https://img-blog.csdnimg.cn/img_convert/311903982dea1d8a5d2c98fc271b5b41.jpeg)



### 学习路线图


 其中最为瞩目也是最为基础的就是网络安全学习路线图,这里我给大家分享一份打磨了3个月,已经更新到4.0版本的网络安全学习路线图。


相比起繁琐的文字,还是生动的视频教程更加适合零基础的同学们学习,这里也是整理了一份与上述学习路线一一对应的网络安全视频教程。


![](https://img-blog.csdnimg.cn/img_convert/1ddfaf7dc5879b1120e31fafa1ad4dc7.jpeg)


#### 网络安全工具箱


当然,当你入门之后,仅仅是视频教程已经不能满足你的需求了,你肯定需要学习各种工具的使用以及大量的实战项目,这里也分享一份**我自己整理的网络安全入门工具以及使用教程和实战。**


![](https://img-blog.csdnimg.cn/img_convert/bcd1787ce996787388468bb227d8f959.jpeg)


#### 项目实战


最后就是项目实战,这里带来的是**SRC资料&HW资料**,毕竟实战是检验真理的唯一标准嘛~


![](https://img-blog.csdnimg.cn/img_convert/35fc46df24091ce3c9a5032a9919b755.jpeg)


#### 面试题


归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
进程同步生产者消费者问题是指在多进程/线程的环境下,生产者和消费者共享同一个资源,但是为了保证数据的正确性和完整性,需要对它们之间的访问进行协调和同步。具体来说,生产者负责向资源中添加数据,而消费者则负责从资源中读取数据,但是由于资源是共享的,因此需要确保生产者和消费者之间的访问不会发生冲突或者丢失数据。 在Java中,可以使用synchronized关键字或者Lock接口来实现进程同步。通常情况下,我们使用一个队列来实现资源的共享,生产者向队列中添加数据,而消费者从队列中取出数据。为了保证同步,我们可以采用以下方法: 1. 使用wait()和notify()方法:在Java中,每个对象都有一个等待队列和一个通知队列。wait()方法可以使线程进入对象的等待队列,并释放对象的锁;notify()方法可以从对象的等待队列中唤醒一个线程,并将其移动到通知队列中。通过使用这两个方法,我们可以实现线程之间的等待和唤醒。 2. 使用Lock接口:Lock接口提供了比synchronized更加灵活的锁机制,可以实现更加细粒度的同步控制。通过使用Lock接口和Condition接口,我们可以实现更加灵活的线程同步控制。 3. 使用阻塞队列:Java中的阻塞队列可以实现线程之间的同步和协调。生产者向队列中添加数据时,如果队列已满,则会被阻塞;消费者从队列中取出数据时,如果队列为空,则会被阻塞。这样可以避免数据丢失或者重复处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值