进程和线程的几种通信方式

进程之间通信的几种方式

1. 管道:是内核里面的一串缓存
管道传输的数据是单向的,若相互进行通信的话,需要进行创建两个管道才行的。
2. 消息队列:
例如,A进程给B进程发送消息,A进程把数据放在对应的消息队列后就可以正常的返回,B进程需要的时候再进行读取数据。
消息队列是保存在内存中的消息链表,在发送数据的时候,会分成一个独立的数据单元,即就是数据块,消息体是用户自定的数据 类型,消息的发送方和接收方要约定好消息体的数据类型,所以每个消息体都是固定大小的存储块,不像管道的无格式的字节流数据,注意的就是,消息队列中读取了消息体的话,内核就会把这个消息体给删除啦。
消息队列不适合比较大数据的传输,因为在内核中每个消息体都有一个最大长度的限制,同时所有队列所包含的全部消息体的总长度也是有上限。在 Linux 内核中,会有两个宏定义 MSGMAXMSGMNB,它们以字节为单位,分别定义了一条消息的最大长度和一个队列的最大长度。

消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销,因为进程写入数据到内核中的消息队列时,会发生从用户态拷贝数据到内核态的过程,同理另一进程读取内核中的消息数据时,会发生从内核态拷贝数据到用户态的过程。

3. 共享内存:
消息队列中的读取和写入的过程,都会有用户态和内核态之间的消息拷贝过程。共享内存的机制就是拿出一块虚拟化的地址空间来,映射到相同的物理内存中。这样这个进程写入东西,另外一个进程就可以看到了,大大提高了进程间通信的速度。
4. 信号量机制:
用了共享内存通信方式,带来新的问题,那就是如果多个进程同时修改同一个共享内存,很有可能就冲突了。例如两个进程都同时写一个地址,那先写的那个进程会发现内容被别人覆盖了。

为了防止多进程竞争共享资源,而造成的数据错乱,所以需要保护机制,使得共享的资源,在任意时刻只能被一个进程访问。正好,信号量就实现了这一保护机制。

信号量其实是一个整型的计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据
信号量表示资源的数量,控制信号量的方式有两种原子操作:

  • 一个是 P 操作,这个操作会把信号量减去 -1,相减后如果信号量 < 0,则表明资源已被占用,进程需阻塞等待;相减后如果信号量 >= 0,则表明还有资源可使用,进程可正常继续执行。
  • 另一个是 V 操作,这个操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是会将该进程唤醒运行;相加后如果信号量 > 0,则表明当前没有阻塞中的进程;

4. 信号:
对于异常的情况下的工作模式,需要用信号的方式来进行通知进程。
信号是进程间通信机制中的唯一的异步通信机制,任何时候给某一进程发送信息,一旦信号产生,就会有这几种的方式:
1、执行默认的操作
2、扑捉信号
3、忽略信号
5. socket:
管道、消息队列、共享内存、信号量和信号是在同一台主机上进行通信的,若想跨网络与不同的主机之间上的进程之间通信,需要用socket套接字啦

线程通信的几种方式

线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

1. 等待通知机制

两个线程通过对同一对象调用等待 wait() 和通知 notify() 方法来进行通讯。

等待通知有着一个经典范式:

线程 A 作为消费者:

  1. 获取对象的锁。
  2. 进入 while(判断条件),并调用 wait() 方法。
  3. 当条件满足跳出循环执行具体处理逻辑。

线程 B 作为生产者:

  1. 获取对象锁。
  2. 更改与线程 A 共用的判断条件。
  3. 调用 notify() 方法。

伪代码如下:

//Thread A

synchronized(Object){
    while(条件){
        Object.wait();
    }
    //do something
}

//Thread B
synchronized(Object){
    条件=false;//改变条件
    Object.notify();
}

2. join() 方法

在 join 线程完成后会调用 notifyAll() 方法,是在 JVM 实现中调用,所以这里看不出来。

3. volatile 共享内存

4. 管道通信

5. 并发工具

  1. CountDownLatch 并发工具
  2. CyclicBarrier 并发工具

Java中创建多线程的几种方式

在这里插入图片描述

方式1:

  1. 继承Thread类
class Number extends Thread{
   
    private static int num=1;

    @Override
    public void run() {
   
//        for (int i = 0; i < 6; i++) {
   
//            System.out.println(Thread.currentThread().getName()+"\t"+i);
//            System.out.println("run thread");
//        }
        System.out.println(Thread.currentThread().getName());
    }
}
public class ThreadTest {
   
    public static void main(String[] args) {
   
        /**
         * 其实在这个地方需要注意的有
         *  一:start()方法是执行相应的准备,然后自动的启动执行run方法的内容调用 start() 方法,
         *          会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了
         *  二:run方法是:会把run方法当成一个main下面的普通的方法去执行的,并不会在某个线程中去执行它的,还是在主线程中进行执行的
         */
        Number number1 = new Number();
        Number number2 = new Number();
        number1.start()
  • 8
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值