-
2019-06-04 16:09:41更多相关内容
-
Java线程间的通信方式详解
2020-09-02 09:08:11主要为大家详细介绍了Java线程间的通信方式,以代码结合文字的方式来讨论线程间的通信,感兴趣的朋友可以参考一下 -
深入理解JAVA多线程之线程间的通信方式
2020-09-02 09:09:24下面小编就为大家带来一篇深入理解JAVA多线程之线程间的通信方式。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 -
线程间的通信方式
2020-09-23 21:54:23所以在现实中,我们需要这些线程间可以按照指定的规则共同完成一件任务,所以这些线程之间就需要互相协调,这个过程被称为线程的通信。 线程的通信可以被定义为: 线程通信就是当多个线程共同操作共享的资源时,...对共享数据进行更改的时候,先到主内存中拷贝一份到本地内存中,然后进行数据的更改,再重新将数据刷到主内存,这中间的过程,其他线程是看不到的。
1、为什么需要线程通信
线程是操作系统调度的最小单位,有自己的栈空间,可以按照既定的代码逐步的执行,但是如果每个线程间都孤立的运行,那就会造资源浪费。所以在现实中,我们需要这些线程间可以按照指定的规则共同完成一件任务,所以这些线程之间就需要互相协调,这个过程被称为线程的通信。
线程的通信可以被定义为:
线程通信就是当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺。
2、线程通信的方式
线程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流。每种方式有不同的方法来实现
- 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式通信。
volatile共享内存
- 消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信。
wait/notify等待通知方式
join方式- 管道流
管道输入/输出流的形式
2.1共享内存
在学习Volatile之前,我们先了解下Java的内存模型,
在java中,所有堆内存中的所有的数据(实例域、静态域和数组元素)存放在主内存中可以在线程之间共享,一些局部变量、方法中定义的参数存放在本地内存中不会在线程间共享。线程之间的共享变量存储在主内存中,本地内存存储了共享变量的副本。如果线程A要和线程B通信,则需要经过以下步骤
①线程A把本地内存A更新过的共享变量刷新到主内存中
②线程B到内存中去读取线程A之前已更新过的共享变量。这保证了线程间的通信必须经过主内存。下面引出我们要学习的关键字volatile
volatile有一个关键的特性:保证内存可见性,即多个线程访问内存中的同一个被volatile关键字修饰的变量时,当某一个线程修改完该变量后,需要先将这个最新修改的值写回到主内存,从而保证下一个读取该变量的线程取得的就是主内存中该数据的最新值,这样就保证线程之间的透明性,便于线程通信。
代码实现
/** * @Author: Simon Lang * @Date: 2020/5/5 15:13 */ public class TestVolatile { private static volatile boolean flag=true; public static void main(String[] args){ new Thread(new Runnable() { public void run() { while (true){ if(flag){ System.out.println("线程A"); flag=false; } } } }).start(); new Thread(new Runnable() { public void run() { while (true){ if(!flag){ System.out.println("线程B"); flag=true; } } } }).start(); } }
测试结果:线程A和线程B交替执行
2.2消息传递
2.2.1wait/notify等待通知方式
从字面上理解,等待通知机制就是将处于等待状态的线程将由其它线程发出通知后重新获取CPU资源,继续执行之前没有执行完的任务。最典型的例子生产者--消费者模式
有一个产品队列,生产者想要在队列中添加产品,消费者需要从队列中取出产品,如果队列为空,消费者应该等待生产者添加产品后才进行消费,队列为满时,生产者需要等待消费者消费一部分产品后才能继续生产。队列可以认为是java模型里的临界资源,生产者和消费者认为是不同的线程,它们需要交替的占用临界资源来进行各自方法的执行,所以就需要线程间通信。
生产者--消费者模型主要为了方便复用和解耦,java语言实现线程之间的通信协作的方式是等待/通知机制
等待/通知机制提供了三个方法用于线程间的通信
wait()当前线程释放锁并进入等待(阻塞)状态notify()唤醒一个正在等待相应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后继续竞争锁notifyAll()唤醒所有正在等待相应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后继续竞争锁
等待/通知机制是指一个线程A调用了对象Object的wait()方法进入等待状态,而另一线程B调用了对象Object的notify()或者notifyAll()方法,当线程A收到通知后就可以从对象Object的wait()方法返回,进而执行后序的操作。线程间的通信需要对象Object来完成,对象中的wait()、notify()、notifyAll()方法就如同开关信号,用来完成等待方和通知方的交互。
测试代码
public class WaitNotify { static boolean flag=true; static Object lock=new Object(); public static void main(String[] args) throws InterruptedException { Thread waitThread=new Thread(new WaitThread(),"WaitThread"); waitThread.start(); TimeUnit.SECONDS.sleep(1); Thread notifyThread=new Thread(new NotifyThread(),"NotifyThread"); notifyThread.start(); } //等待线程 static class WaitThread implements Runnable{ public void run() { //加锁 synchronized (lock){ //条件不满足时,继续等待,同时释放lock锁 while (flag){ System.out.println("flag为true,不满足条件,继续等待"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //条件满足 System.out.println("flag为false,我要从wait状态返回继续执行了"); } } } //通知线程 static class NotifyThread implements Runnable{ public void run() { //加锁 synchronized (lock){ //获取lock锁,然后进行通知,但不会立即释放lock锁,需要该线程执行完毕 lock.notifyAll(); System.out.println("设置flag为false,我发出通知了,但是我不会立马释放锁"); flag=false; } } } }
测试结果
NOTE:使用wait()、notify()和notifyAll()需要注意以下细节
- 使用wait()、notify()和notifyAll()需要先调用对象加锁
- 调用wait()方法后,线程状态由Running变成Waiting,并将当前线程放置到对象的等待队列
- notify()和notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()和notifyAll()的线程释放锁之后等待线程才有机会从wait()返回
- notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()方法则是将等待队列中所有的线程全部转移到同步队列,被移到的线程状态由Waiting变为Blocked。
- 从wait()方法返回的前提是获得调用对象的锁
其实等待通知机制有有一个经典的范式,该范式可以分为两部分,分别是等待方(消费者)和通知方(生产者)
- 等待方
synchronized(对象){ while(条件不满足){ 对象.wait() } 对应的处理逻辑 }
- 通知方
synchronized(对象){ 改变条件 对象.notifyAll }
2.2.2join方式
在很多应用场景中存在这样一种情况,主线程创建并启动子线程后,如果子线程要进行很耗时的计算,那么主线程将比子线程先结束,但是主线程需要子线程的计算的结果来进行自己下一步的计算,这时主线程就需要等待子线程,java中提供可join()方法解决这个问题。
join()方法的作用是:在当前线程A调用线程B的join()方法后,会让当前线程A阻塞,直到线程B的逻辑执行完成,A线程才会解除阻塞,然后继续执行自己的业务逻辑,这样做可以节省计算机中资源。
测试代码
public class TestJoin { public static void main(String[] args){ Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println("线程0开始执行了"); } }); thread.start(); for (int i=0;i<10;i++){ JoinThread jt=new JoinThread(thread,i); jt.start(); thread=jt; } } static class JoinThread extends Thread{ private Thread thread; private int i; public JoinThread(Thread thread,int i){ this.thread=thread; this.i=i; } @Override public void run() { try { thread.join(); System.out.println("线程"+(i+1)+"执行了"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
测试结果
NOTE:每个线程的终止的前提是前驱线程的终止,每个线程等待前驱线程终止后,才从join方法返回,实际上,这里涉及了等待/通知机制,即下一个线程的执行需要接受前驱线程结束的通知。
2.3管道输入/输出流
管道流是是一种使用比较少的线程间通信方式,管道输入/输出流和普通文件输入/输出流或者网络输出/输出流不同之处在于,它主要用于线程之间的数据传输,传输的媒介为管道。
管道输入/输出流主要包括4种具体的实现:PipedOutputStrean、PipedInputStrean、PipedReader和PipedWriter,前两种面向字节,后两种面向字符。
java的管道的输入和输出实际上使用的是一个循环缓冲数组来实现的,默认为1024,输入流从这个数组中读取数据,输出流从这个数组中写入数据,当这个缓冲数组已满的时候,输出流所在的线程就会被阻塞,当向这个缓冲数组为空时,输入流所在的线程就会被阻塞。
buffer:缓冲数组,默认为1024
out:从缓冲数组中读数据
in:从缓冲数组中写数据测试代码
public class TestPip { public static void main(String[] args) throws IOException { PipedWriter writer = new PipedWriter(); PipedReader reader = new PipedReader(); //使用connect方法将输入流和输出流连接起来 writer.connect(reader); Thread printThread = new Thread(new Print(reader) , "PrintThread"); //启动线程printThread printThread.start(); int receive = 0; try{ //读取输入的内容 while((receive = System.in.read()) != -1){ writer.write(receive); } }finally { writer.close(); } } private static class Print implements Runnable { private PipedReader reader; public Print(PipedReader reader) { this.reader = reader; } @Override public void run() { int receive = 0; try{ while ((receive = reader.read()) != -1){ //字符转换 System.out.print((char) receive); } }catch (IOException e) { System.out.print(e); } } } }
测试结果
NOTE:对于Piped类型的流,必须先进性绑定,也就是调用connect()方法,如果没有将输入/输出流绑定起来,对于该流的访问将抛出异常。
-
Android进程间和线程间通信方式
2021-01-17 13:22:41线程自己基本上不拥有系统资源,只拥有一些在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。区别:(1)、一个程序至少有一个进程,一个进...进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一些在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
区别:
(1)、一个程序至少有一个进程,一个进程至少有一个线程;
(2)、线程的划分尺度小于进程,使得多线程程序的并发性高;
(3)、进程在执行过程中拥有独立的内存单元,而多个线程共享内存,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。
---------------------
一、Android进程间通信方式
1.Bundle
由于Activity,Service,Receiver都是可以通过Intent来携带Bundle传输数据的,所以我们可以在一个进程中通过Intent将携带数据的Bundle发送到另一个进程的组件。
缺点:无法传输Bundle不支持的数据类型。
2.ContentProvider
ContentProvider是Android四大组件之一,以表格的方式来储存数据,提供给外界,即Content Provider可以跨进程访问其他应用程序中的数据。用法是继承ContentProvider,实现onCreate,query,update,insert,delete和getType方法,onCreate是负责创建时做一些初始化的工作,增删查改的方法就是对数据的查询和修改,getType是返回一个String,表示Uri请求的类型。注册完后就可以使用ContentResolver去请求指定的Uri。
3.文件
两个进程可以到同一个文件去交换数据,我们不仅可以保存文本文件,还可以将对象持久化到文件,从另一个文件恢复。要注意的是,当并发读/写时可能会出现并发的问题。
4.Broadcast
Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播。
5.AIDL方式
Service和Content Provider类似,也可以访问其他应用程序中的数据,Content Provider返回的是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。
AIDL通过定义服务端暴露的接口,以提供给客户端来调用,AIDL使服务器可以并行处理,而Messenger封装了AIDL之后只能串行运行,所以Messenger一般用作消息传递。
6.Messenger
Messenger是基于AIDL实现的,服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。
双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。
7.Socket
Socket方法是通过网络来进行数据交换,注意的是要在子线程请求,不然会堵塞主线程。客户端和服务端建立连接之后即可不断传输数据,比较适合实时的数据传输
二、Android线程间通信方式
一般说线程间通信主要是指主线程(也叫UI线程)和子线程之间的通信,主要有以下两种方式:
1.AsyncTask机制
AsyncTask,异步任务,也就是说在UI线程运行的时候,可以在后台的执行一些异步的操作;AsyncTask可以很容易且正确地使用UI线程,AsyncTask允许进行后台操作,并在不显示使用工作线程或Handler机制的情况下,将结果反馈给UI线程。但是AsyncTask只能用于短时间的操作(最多几秒就应该结束的操作),如果需要长时间运行在后台,就不适合使用AsyncTask了,只能去使用Java提供的其他API来实现。
2.Handler机制
Handler,继承自Object类,用来发送和处理Message对象或Runnable对象;Handler在创建时会与当前所在的线程的Looper对象相关联(如果当前线程的Looper为空或不存在,则会抛出异常,此时需要在线程中主动调用Looper.prepare()来创建一个Looper对象)。使用Handler的主要作用就是在后面的过程中发送和处理Message对象和让其他的线程完成某一个动作(如在工作线程中通过Handler对象发送一个Message对象,让UI线程进行UI的更新,然后UI线程就会在MessageQueue中得到这个Message对象(取出Message对象是由其相关联的Looper对象完成的),并作出相应的响应)。
三、Android两个子线程之间通信
面试的过程中,有些面试官可能会问Android子线程之间的通信方式,由于绝大部分程序员主要关注的是Android主线程和子线程之间的通信,所以这个问题很容易让人懵逼。
主线程和子线程之间的通信可以通过主线程中的handler把子线程中的message发给主线程中的looper,或者,主线程中的handler通过post向looper中发送一个runnable。但looper默认存在于main线程中,子线程中没有Looper,该怎么办呢?其实原理很简单,把looper绑定到子线程中,并且创建一个handler。在另一个线程中通过这个handler发送消息,就可以实现子线程之间的通信了。
子线程创建handler的两种方式:
方式一:给子线程创建Looper对象:
new Thread(new Runnable() {
public void run() {
Looper.prepare(); // 给这个Thread创建Looper对象,一个Thead只有一个Looper对象
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), "handleMessage", Toast.LENGTH_LONG).show();
}
};
handler.sendEmptyMessage(1);
Looper.loop(); // 不断遍历MessageQueue中是否有消息
};
}).start();
---------------------
方式二:获取主线程的looper,或者说是UI线程的looper:
new Thread(new Runnable() {
public void run() {
Handler handler = new Handler(Looper.getMainLooper()){ // 区别在这!!!
@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), "handleMessage", Toast.LENGTH_LONG).show();
}
};
handler.sendEmptyMessage(1);
};
}).start();
---------------------
-
线程间通信方式3:消息传递方式
2013-01-18 11:01:23线程间通信方式3:消息传递方式的演示。采用计算演示线程的执行,并采用用户界面线程来实时显示执行的进度,线程间的通信方式采用了3种方式相结合,对多线程间的通信有比较好的学习和研究价值。 -
c++ 线程间通信方式
2020-01-09 20:37:49一:两个进程间的两个线程通信,相当于进程间通信 二:一个进程中的两个线程间通信 通信方式: 1.互斥锁 mutex; lock_guard (在构造函数里加锁,在析构函数里解锁) unique_lock自动加锁、解锁 2....一:两个进程间的两个线程通信,相当于进程间通信
二:一个进程中的两个线程间通信
通信方式:
1.互斥锁
mutex;
lock_guard (在构造函数里加锁,在析构函数里解锁)
unique_lock
自动加锁、解锁2.读写锁
shared_lock
3.信号量
c++11中未实现,可以自己使用mutex和conditon_variable 实现
代码实现如下:
#pragma once
#include <mutex>
#include <condition_variable>
class Semaphore
{
public:
explicit Semaphore(unsigned int count); //用无符号数表示信号量资源
~Semaphore();public:
void wait();
void signal();
private:
int m_count; //计数器必须是有符号数
std::mutex m_mutex;
std::condition_variable m_condition_variable;
};#include "Semaphore.h"
Semaphore::Semaphore(unsigned int count) :m_count(count) {
}Semaphore::~Semaphore()
{
}void Semaphore::wait() {
std::unique_lock<std::mutex> unique_lock(m_mutex);
--m_count;
while (m_count < 0) {
m_condition_variable.wait(unique_lock);
}
}void Semaphore::signal() {
std::lock_guard<std::mutex> lg(m_mutex);
if (++m_count < 1) {
m_condition_variable.notify_one();
}
}4.条件变量
condition_variable
-
进程间通信方式和线程间通信方式
2019-11-03 13:09:13进程间通信方式: 1.管道( pipe ): 管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。缺点:速度慢,容量有限,只有父子进程能通讯 2.... -
Python线程间通信方式
2019-06-04 20:16:301、python多线程 #! /usr/bin/evn python3 # --*-- coding: utf-8 --*-- #该实例反编译来说明函数执行流程 import dis def add(a): a = a+1 return a print(dis.dis(add)) # Python中一个线程对应于C语言中的... -
线程间通信方式2:参数传递方式
2013-01-11 11:17:13线程间通信方式2:参数传递方式。通过3类线程的创建方法,演示了给线程传递方式的方式,包括;单参数、多参数和类3类。 -
Linux的进程/线程间通信方式总结
2021-05-10 05:55:17Linux系统中的进程间通信方式主要以下几种:同一主机上的进程通信方式* UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal)* System V进程通信方式:包括信号量(Semaphore), 消息队列(Message ... -
android进、线程间通信方式
2022-03-22 12:07:312.线程间的通信 EventBus的使用 1.自定义一个类,表示需要发送的实体类 public class EventBusSend { public EventBusSend(){} } 2.发送消息页面 eventBus.post(new EventBusSend event); 3.接受消息页面 ... -
进程间、线程间通信方式
2019-09-18 22:05:15一、进程间的通信方式 1.管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 2.有名管道 (namedpipe) : 有名管道也是半双工... -
并发理论:线程间通信方式有哪些?
2022-02-15 14:56:10共享内存 volatile关键字 等待通知 wait,notify 为什么要和synchronized一起使用? 当线程要获取的资源没有时,线程需要等待...sleep是单纯的等待方法,和wait是用来进行线程通信的方法,当资源得不到满足时不得不等. -
Android进程间通信方式与线程间通信方式的列举
2019-11-23 11:58:25本文只是列举了Android进程间通信和Android线程间通信的常见方式,还请见谅,具体实现或原理可参见其他博客。 Android IPC Intent方式Bundle通信 Broadcast方式 文件共享的方式 AIDL方式(基于Binder) ... -
面试题:进程间通信方式,线程间通信方式
2020-12-21 22:40:53一、进程间通信(IPC,Inter-Process Communication)是指在不同进程间传播或交换信息 1. 无名管道 特点 半双工(数据流向仅有一个方向),具有固定的读端和写端 只能用于父进程或兄弟线程之间通信(具有血缘关系的... -
Python如何实现线程间通信
2020-09-16 11:01:47主要介绍了Python如何实现线程间通信,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下 -
Java实现线程间的通信的五种方式
2021-07-06 21:08:24如何使两个线程按顺序执行? 假设有两个线程:线程A和线程B。两个线程都可以依次打印三个数字(1-3)。让我们看一下代码: private static void demo1() { Thread A = new Thread(new Runnable() { @Override ... -
进程和线程间通信的方式总结
2020-08-05 16:53:24进程间通信的方式: 进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。 管道: 管道主要包括无名管道和命名管道:管道可用于具有亲缘关系的父子进程间的通信,有名... -
python的多线程及线程间的通信方式
2020-11-04 22:56:15") 在上述线程中,t1和t2两个线程共享了变量var,不过使用这种方式时,需要注意线程之间的同步问题(具体参见《生产者消费者模式的实现》) 上述程序的执行结果如下: >使用Queue实现 from queue import Queue from ... -
Java中线程之间的通信方式
2020-08-19 09:47:01对于线程间通信来说,线程间同步可以归纳为线程间通信的一个子集,对于线程通信指的是两个线程之间可以交换一些实时的数据信息,而线程同步只交换一些控制信息。 1 synchronized package Communication; class ... -
C例子:线程间通信
2016-01-24 23:28:05该程序是我写的博客“一起talk C栗子吧(第一百一十一回:C语言实例--线程间通信)”的配套程序,共享给大家使用 -
多线程之间通信的5种方式
2020-01-05 15:11:39首先,我们知道线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的; 以客户卖包子为例,当店家在包子加工到第4步时就可以卖给客户为例说明,A,B两个线程,示例如下: 方式一:使用... -
线程之间的通信方式
2021-08-02 16:01:05前言 我只是个搬运工,尊重原作者的劳动成果,本文来源下列文章链接: ...首先,要短信线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析: 题目 -
Android线程间通讯的几种方式
2021-06-06 10:16:221.runOnUiThread(Runnable) 在子线程中直接使用该方法,可以更新UIrunOnUiThread(new Runnable(){//更新UI@Overridepublic void run() {publish_time.setText("更新失败");}});2.View.postDelay(Runnable , long)/... -
Linux进程间通信-线程间通信
2021-05-08 23:29:121、管道管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。2、 消息队列消息队列用于运行于同一台机器上的进程间通信,它和... -
Linux 线程间通信方式、进程通信方式
2021-10-22 14:39:21Linux线程间通信几种主要的手段 1. 管道: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系... -
进程间通信和线程间通信的几种方式及区别
2021-11-22 19:11:02而线程,相对于进程而言,是一个更加接近于执行体的概念,可以和同进程的其他线程之间直接共享数据,而且拥有自己的栈空间,拥有独立序列。 共同点:它们都能提高程序的并发度,提高程序运行效率和响应时间。线程和...